1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""distutils.command.bdist_wininst
2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew HsiehImplements the Distutils 'bdist_wininst' command: create a windows installer
4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehexe-program."""
5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh__revision__ = "$Id$"
7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport sys
9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport os
10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport string
11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom sysconfig import get_python_version
13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom distutils.core import Command
15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom distutils.dir_util import remove_tree
16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom distutils.errors import DistutilsOptionError, DistutilsPlatformError
17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom distutils import log
18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom distutils.util import get_platform
19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass bdist_wininst (Command):
21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    description = "create an executable installer for MS Windows"
23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    user_options = [('bdist-dir=', None,
25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "temporary directory for creating the distribution"),
26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('plat-name=', 'p',
27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "platform name to embed in generated filenames "
28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "(default: %s)" % get_platform()),
29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('keep-temp', 'k',
30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "keep the pseudo-installation tree around after " +
31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "creating the distribution archive"),
32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('target-version=', None,
33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "require a specific python version" +
34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     " on the target system"),
35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('no-target-compile', 'c',
36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "do not compile .py to .pyc on the target system"),
37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('no-target-optimize', 'o',
38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "do not compile .py to .pyo (optimized)"
39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "on the target system"),
40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('dist-dir=', 'd',
41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "directory to put final built distributions in"),
42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('bitmap=', 'b',
43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "bitmap to use for the installer instead of python-powered logo"),
44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('title=', 't',
45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "title to display on the installer background instead of default"),
46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('skip-build', None,
47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "skip rebuilding everything (for testing/debugging)"),
48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('install-script=', None,
49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "basename of installation script to be run after"
50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "installation or before deinstallation"),
51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('pre-install-script=', None,
52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "Fully qualified filename of a script to be run before "
53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "any files are installed.  This script need not be in the "
54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "distribution"),
55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    ('user-access-control=', None,
56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "specify Vista's UAC handling - 'none'/default=no "
57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "handling, 'auto'=use UAC if target Python installed for "
58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "all users, 'force'=always use UAC"),
59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                   ]
60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                       'skip-build']
63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def initialize_options (self):
65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.bdist_dir = None
66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.plat_name = None
67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.keep_temp = 0
68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.no_target_compile = 0
69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.no_target_optimize = 0
70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.target_version = None
71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.dist_dir = None
72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.bitmap = None
73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.title = None
74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.skip_build = None
75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.install_script = None
76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.pre_install_script = None
77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.user_access_control = None
78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # initialize_options()
80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def finalize_options (self):
83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.bdist_dir is None:
86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self.skip_build and self.plat_name:
87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # If build is skipped and plat_name is overridden, bdist will
88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # not see the correct 'plat_name' - so set that up manually.
89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                bdist = self.distribution.get_command_obj('bdist')
90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                bdist.plat_name = self.plat_name
91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                # next the command will be initialized using that name
92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bdist_base = self.get_finalized_command('bdist').bdist_base
93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.bdist_dir = os.path.join(bdist_base, 'wininst')
94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.target_version:
96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.target_version = ""
97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.skip_build and self.distribution.has_ext_modules():
99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            short_version = get_python_version()
100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self.target_version and self.target_version != short_version:
101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                raise DistutilsOptionError, \
102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      "target version can only be %s, or the '--skip-build'" \
103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      " option must be specified" % (short_version,)
104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.target_version = short_version
105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.set_undefined_options('bdist',
107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                   ('dist_dir', 'dist_dir'),
108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                   ('plat_name', 'plat_name'),
109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                  )
110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.install_script:
112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            for script in self.distribution.scripts:
113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if self.install_script == os.path.basename(script):
114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    break
115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                raise DistutilsOptionError, \
117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      "install_script '%s' not found in scripts" % \
118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                      self.install_script
119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # finalize_options()
120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def run (self):
123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if (sys.platform != "win32" and
124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            (self.distribution.has_ext_modules() or
125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh             self.distribution.has_c_libraries())):
126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            raise DistutilsPlatformError \
127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                  ("distribution contains extensions and/or C libraries; "
128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                   "must be compiled on a Windows 32 platform")
129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.skip_build:
131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            self.run_command('build')
132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install = self.reinitialize_command('install', reinit_subcommands=1)
134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.root = self.bdist_dir
135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.skip_build = self.skip_build
136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.warn_dir = 0
137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.plat_name = self.plat_name
138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install_lib = self.reinitialize_command('install_lib')
140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # we do not want to include pyc or pyo files
141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install_lib.compile = 0
142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install_lib.optimize = 0
143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.distribution.has_ext_modules():
145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # If we are building an installer for a Python version other
146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # than the one we are currently running, then we need to ensure
147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # our build_lib reflects the other Python version rather than ours.
148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # Note that for target_version!=sys.version, we must have skipped the
149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # build step, so there is no issue with enforcing the build of this
150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # version.
151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            target_version = self.target_version
152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if not target_version:
153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                assert self.skip_build, "Should have already checked this"
154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                target_version = sys.version[0:3]
155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            plat_specifier = ".%s-%s" % (self.plat_name, target_version)
156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            build = self.get_finalized_command('build')
157ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            build.build_lib = os.path.join(build.build_base,
158ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                           'lib' + plat_specifier)
159ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
160ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Use a custom scheme for the zip-file, because we have to decide
161ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # at installation time which scheme to use.
162ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
163ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            value = string.upper(key)
164ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if key == 'headers':
165ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                value = value + '/Include/$dist_name'
166ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            setattr(install,
167ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    'install_' + key,
168ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    value)
169ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
170ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        log.info("installing to %s", self.bdist_dir)
171ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.ensure_finalized()
172ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
173ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # avoid warning of 'install_lib' about installing
174ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # into a directory not in sys.path
175ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
176ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
177ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        install.run()
178ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
179ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        del sys.path[0]
180ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
181ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # And make an archive relative to the root of the
182ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # pseudo-installation tree.
183ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        from tempfile import mktemp
184ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        archive_basename = mktemp()
185ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        fullname = self.distribution.get_fullname()
186ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        arcname = self.make_archive(archive_basename, "zip",
187ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                    root_dir=self.bdist_dir)
188ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # create an exe containing the zip-file
189ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.create_exe(arcname, fullname, self.bitmap)
190ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.distribution.has_ext_modules():
191ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pyversion = get_python_version()
192ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
193ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pyversion = 'any'
194ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.distribution.dist_files.append(('bdist_wininst', pyversion,
195ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                             self.get_installer_filename(fullname)))
196ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # remove the zip-file again
197ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        log.debug("removing temporary file '%s'", arcname)
198ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        os.remove(arcname)
199ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
200ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if not self.keep_temp:
201ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            remove_tree(self.bdist_dir, dry_run=self.dry_run)
202ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
203ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # run()
204ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
205ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def get_inidata (self):
206ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Return data describing the installation.
207ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
208ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines = []
209ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        metadata = self.distribution.metadata
210ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
211ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Write the [metadata] section.
212ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("[metadata]")
213ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
214ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # 'info' will be displayed in the installer's dialog box,
215ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # describing the items to be installed.
216ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        info = (metadata.long_description or '') + '\n'
217ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
218ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Escape newline characters
219ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        def escape(s):
220ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return string.replace(s, "\n", "\\n")
221ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
222ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        for name in ["author", "author_email", "description", "maintainer",
223ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     "maintainer_email", "name", "url", "version"]:
224ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            data = getattr(metadata, name, "")
225ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if data:
226ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                info = info + ("\n    %s: %s" % \
227ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                               (string.capitalize(name), escape(data)))
228ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                lines.append("%s=%s" % (name, escape(data)))
229ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
230ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # The [setup] section contains entries controlling
231ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # the installer runtime.
232ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("\n[Setup]")
233ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.install_script:
234ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lines.append("install_script=%s" % self.install_script)
235ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("info=%s" % escape(info))
236ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("target_compile=%d" % (not self.no_target_compile))
237ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("target_optimize=%d" % (not self.no_target_optimize))
238ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.target_version:
239ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lines.append("target_version=%s" % self.target_version)
240ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.user_access_control:
241ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            lines.append("user_access_control=%s" % self.user_access_control)
242ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
243ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        title = self.title or self.distribution.get_fullname()
244ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("title=%s" % escape(title))
245ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import time
246ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import distutils
247ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        build_info = "Built %s with distutils-%s" % \
248ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                     (time.ctime(time.time()), distutils.__version__)
249ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        lines.append("build_info=%s" % build_info)
250ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return string.join(lines, "\n")
251ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
252ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # get_inidata()
253ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
254ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def create_exe (self, arcname, fullname, bitmap=None):
255ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        import struct
256ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
257ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.mkpath(self.dist_dir)
258ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
259ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        cfgdata = self.get_inidata()
260ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
261ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        installer_name = self.get_installer_filename(fullname)
262ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        self.announce("creating %s" % installer_name)
263ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
264ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if bitmap:
265ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bitmapdata = open(bitmap, "rb").read()
266ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bitmaplen = len(bitmapdata)
267ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
268ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bitmaplen = 0
269ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
270ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        file = open(installer_name, "wb")
271ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        file.write(self.get_exe_bytes())
272ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if bitmap:
273ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            file.write(bitmapdata)
274ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
275ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Convert cfgdata from unicode to ascii, mbcs encoded
276ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
277ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            unicode
278ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        except NameError:
279ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            pass
280ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
281ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if isinstance(cfgdata, unicode):
282ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                cfgdata = cfgdata.encode("mbcs")
283ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
284ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Append the pre-install script
285ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        cfgdata = cfgdata + "\0"
286ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.pre_install_script:
287ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            script_data = open(self.pre_install_script, "r").read()
288ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            cfgdata = cfgdata + script_data + "\n\0"
289ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
290ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # empty pre-install script
291ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            cfgdata = cfgdata + "\0"
292ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        file.write(cfgdata)
293ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
294ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # The 'magic number' 0x1234567B is used to make sure that the
295ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # binary layout of 'cfgdata' is what the wininst.exe binary
296ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # expects.  If the layout changes, increment that number, make
297ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # the corresponding changes to the wininst.exe sources, and
298ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # recompile them.
299ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        header = struct.pack("<iii",
300ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                             0x1234567B,       # tag
301ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                             len(cfgdata),     # length
302ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                             bitmaplen,        # number of bytes in bitmap
303ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                             )
304ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        file.write(header)
305ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        file.write(open(arcname, "rb").read())
306ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
307ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # create_exe()
308ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
309ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def get_installer_filename(self, fullname):
310ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Factored out to allow overriding in subclasses
311ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.target_version:
312ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # if we create an installer for a specific python version,
313ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # it's better to include this in the name
314ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            installer_name = os.path.join(self.dist_dir,
315ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                          "%s.%s-py%s.exe" %
316ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                           (fullname, self.plat_name, self.target_version))
317ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
318ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            installer_name = os.path.join(self.dist_dir,
319ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                                          "%s.%s.exe" % (fullname, self.plat_name))
320ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        return installer_name
321ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    # get_installer_filename()
322ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
323ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh    def get_exe_bytes (self):
324ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        from distutils.msvccompiler import get_build_version
325ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # If a target-version other than the current version has been
326ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # specified, then using the MSVC version from *this* build is no good.
327ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # Without actually finding and executing the target version and parsing
328ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # its sys.version, we just hard-code our knowledge of old versions.
329ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # NOTE: Possible alternative is to allow "--target-version" to
330ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # specify a Python executable rather than a simple version string.
331ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # We can then execute this program to obtain any info we need, such
332ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # as the real sys.version string for the build.
333ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        cur_version = get_python_version()
334ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.target_version and self.target_version != cur_version:
335ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # If the target version is *later* than us, then we assume they
336ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # use what we use
337ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # string compares seem wrong, but are what sysconfig.py itself uses
338ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            if self.target_version > cur_version:
339ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                bv = get_build_version()
340ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            else:
341ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                if self.target_version < "2.4":
342ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    bv = 6.0
343ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                else:
344ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh                    bv = 7.1
345ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
346ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            # for current version - use authoritative check.
347ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            bv = get_build_version()
348ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
349ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # wininst-x.y.exe is in the same directory as this file
350ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        directory = os.path.dirname(__file__)
351ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # we must use a wininst-x.y.exe built with the same C compiler
352ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # used for python.  XXX What about mingw, borland, and so on?
353ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
354ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # if plat_name starts with "win" but is not "win32"
355ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # we want to strip "win" and leave the rest (e.g. -amd64)
356ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        # for all other cases, we don't want any suffix
357ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        if self.plat_name != 'win32' and self.plat_name[:3] == 'win':
358ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sfix = self.plat_name[3:]
359ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        else:
360ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            sfix = ''
361ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh
362ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix))
363ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        f = open(filename, "rb")
364ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        try:
365ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            return f.read()
366ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh        finally:
367ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh            f.close()
368ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# class bdist_wininst
369