Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 1 | """distutils.command.bdist_wininst |
| 2 | |
| 3 | Implements the Distutils 'bdist_wininst' command: create a windows installer |
| 4 | exe-program.""" |
| 5 | |
Miss Islington (bot) | b4cd6ba | 2019-07-05 02:03:23 -0700 | [diff] [blame^] | 6 | import os |
| 7 | import sys |
| 8 | import warnings |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 9 | from distutils.core import Command |
Tarek Ziadé | 8b441d0 | 2010-01-29 11:46:31 +0000 | [diff] [blame] | 10 | from distutils.util import get_platform |
Tarek Ziadé | 3679727 | 2010-07-22 12:50:05 +0000 | [diff] [blame] | 11 | from distutils.dir_util import create_tree, remove_tree |
| 12 | from distutils.errors import * |
| 13 | from distutils.sysconfig import get_python_version |
| 14 | from distutils import log |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 15 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 16 | class bdist_wininst(Command): |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 17 | |
Greg Ward | b0b98a5 | 2000-06-28 00:56:20 +0000 | [diff] [blame] | 18 | description = "create an executable installer for MS Windows" |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 19 | |
Thomas Heller | 5c5ea1a | 2001-03-16 20:57:37 +0000 | [diff] [blame] | 20 | user_options = [('bdist-dir=', None, |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 21 | "temporary directory for creating the distribution"), |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 22 | ('plat-name=', 'p', |
| 23 | "platform name to embed in generated filenames " |
| 24 | "(default: %s)" % get_platform()), |
Greg Ward | 3bfc8c8 | 2000-09-16 15:56:32 +0000 | [diff] [blame] | 25 | ('keep-temp', 'k', |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 26 | "keep the pseudo-installation tree around after " + |
| 27 | "creating the distribution archive"), |
Thomas Heller | a146fea | 2004-07-06 19:23:27 +0000 | [diff] [blame] | 28 | ('target-version=', None, |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 29 | "require a specific python version" + |
Greg Ward | 018cbb1 | 2000-08-26 02:40:10 +0000 | [diff] [blame] | 30 | " on the target system"), |
Thomas Heller | 904ca11 | 2000-09-07 15:59:22 +0000 | [diff] [blame] | 31 | ('no-target-compile', 'c', |
| 32 | "do not compile .py to .pyc on the target system"), |
| 33 | ('no-target-optimize', 'o', |
Serhiy Storchaka | 34fd4c2 | 2018-11-05 16:20:25 +0200 | [diff] [blame] | 34 | "do not compile .py to .pyo (optimized) " |
Thomas Heller | 1dbe9d5 | 2000-09-29 11:36:55 +0000 | [diff] [blame] | 35 | "on the target system"), |
Greg Ward | fd9f168 | 2000-07-05 03:08:55 +0000 | [diff] [blame] | 36 | ('dist-dir=', 'd', |
| 37 | "directory to put final built distributions in"), |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 38 | ('bitmap=', 'b', |
| 39 | "bitmap to use for the installer instead of python-powered logo"), |
| 40 | ('title=', 't', |
| 41 | "title to display on the installer background instead of default"), |
Martin v. Löwis | 9668b93 | 2002-01-12 11:27:42 +0000 | [diff] [blame] | 42 | ('skip-build', None, |
| 43 | "skip rebuilding everything (for testing/debugging)"), |
Thomas Heller | d2d58e0 | 2002-02-20 08:01:19 +0000 | [diff] [blame] | 44 | ('install-script=', None, |
Serhiy Storchaka | 34fd4c2 | 2018-11-05 16:20:25 +0200 | [diff] [blame] | 45 | "basename of installation script to be run after " |
Thomas Heller | d7c14c6 | 2002-11-05 10:06:19 +0000 | [diff] [blame] | 46 | "installation or before deinstallation"), |
Thomas Heller | a19cdad | 2004-02-20 14:43:21 +0000 | [diff] [blame] | 47 | ('pre-install-script=', None, |
| 48 | "Fully qualified filename of a script to be run before " |
| 49 | "any files are installed. This script need not be in the " |
| 50 | "distribution"), |
Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 51 | ('user-access-control=', None, |
| 52 | "specify Vista's UAC handling - 'none'/default=no " |
| 53 | "handling, 'auto'=use UAC if target Python installed for " |
| 54 | "all users, 'force'=always use UAC"), |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 55 | ] |
| 56 | |
Andrew M. Kuchling | da9f0bf | 2002-03-21 23:44:01 +0000 | [diff] [blame] | 57 | boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', |
| 58 | 'skip-build'] |
Greg Ward | 99b032e | 2000-09-25 01:41:15 +0000 | [diff] [blame] | 59 | |
Miss Islington (bot) | be5bb52 | 2019-07-01 05:54:19 -0700 | [diff] [blame] | 60 | # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows |
| 61 | _unsupported = (sys.platform != "win32") |
| 62 | |
Miss Islington (bot) | b4cd6ba | 2019-07-05 02:03:23 -0700 | [diff] [blame^] | 63 | def __init__(self, *args, **kw): |
| 64 | super().__init__(*args, **kw) |
| 65 | warnings.warn("bdist_wininst command is deprecated since Python 3.8, " |
| 66 | "use bdist_wheel (wheel packages) instead", |
| 67 | DeprecationWarning, 2) |
| 68 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 69 | def initialize_options(self): |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 70 | self.bdist_dir = None |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 71 | self.plat_name = None |
Greg Ward | 3bfc8c8 | 2000-09-16 15:56:32 +0000 | [diff] [blame] | 72 | self.keep_temp = 0 |
Thomas Heller | 904ca11 | 2000-09-07 15:59:22 +0000 | [diff] [blame] | 73 | self.no_target_compile = 0 |
| 74 | self.no_target_optimize = 0 |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 75 | self.target_version = None |
Greg Ward | fd9f168 | 2000-07-05 03:08:55 +0000 | [diff] [blame] | 76 | self.dist_dir = None |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 77 | self.bitmap = None |
| 78 | self.title = None |
Éric Araujo | fbe37df | 2011-08-29 21:48:39 +0200 | [diff] [blame] | 79 | self.skip_build = None |
Thomas Heller | d2d58e0 | 2002-02-20 08:01:19 +0000 | [diff] [blame] | 80 | self.install_script = None |
Thomas Heller | a19cdad | 2004-02-20 14:43:21 +0000 | [diff] [blame] | 81 | self.pre_install_script = None |
Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 82 | self.user_access_control = None |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 83 | |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 84 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 85 | def finalize_options(self): |
Éric Araujo | fbe37df | 2011-08-29 21:48:39 +0200 | [diff] [blame] | 86 | self.set_undefined_options('bdist', ('skip_build', 'skip_build')) |
| 87 | |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 88 | if self.bdist_dir is None: |
Georg Brandl | f08a9dd | 2008-06-10 16:57:31 +0000 | [diff] [blame] | 89 | if self.skip_build and self.plat_name: |
| 90 | # If build is skipped and plat_name is overridden, bdist will |
| 91 | # not see the correct 'plat_name' - so set that up manually. |
| 92 | bdist = self.distribution.get_command_obj('bdist') |
| 93 | bdist.plat_name = self.plat_name |
| 94 | # next the command will be initialized using that name |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 95 | bdist_base = self.get_finalized_command('bdist').bdist_base |
| 96 | self.bdist_dir = os.path.join(bdist_base, 'wininst') |
Éric Araujo | fbe37df | 2011-08-29 21:48:39 +0200 | [diff] [blame] | 97 | |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 98 | if not self.target_version: |
| 99 | self.target_version = "" |
Éric Araujo | fbe37df | 2011-08-29 21:48:39 +0200 | [diff] [blame] | 100 | |
Thomas Heller | a19cdad | 2004-02-20 14:43:21 +0000 | [diff] [blame] | 101 | if not self.skip_build and self.distribution.has_ext_modules(): |
Andrew M. Kuchling | 1cace1a | 2002-11-14 01:44:35 +0000 | [diff] [blame] | 102 | short_version = get_python_version() |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 103 | if self.target_version and self.target_version != short_version: |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 104 | raise DistutilsOptionError( |
Georg Brandl | 56be37c | 2010-08-02 19:16:34 +0000 | [diff] [blame] | 105 | "target version can only be %s, or the '--skip-build'" \ |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 106 | " option must be specified" % (short_version,)) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 107 | self.target_version = short_version |
| 108 | |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 109 | self.set_undefined_options('bdist', |
| 110 | ('dist_dir', 'dist_dir'), |
| 111 | ('plat_name', 'plat_name'), |
| 112 | ) |
Greg Ward | fd9f168 | 2000-07-05 03:08:55 +0000 | [diff] [blame] | 113 | |
Thomas Heller | d7c14c6 | 2002-11-05 10:06:19 +0000 | [diff] [blame] | 114 | if self.install_script: |
| 115 | for script in self.distribution.scripts: |
| 116 | if self.install_script == os.path.basename(script): |
| 117 | break |
| 118 | else: |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 119 | raise DistutilsOptionError( |
| 120 | "install_script '%s' not found in scripts" |
| 121 | % self.install_script) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 122 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 123 | def run(self): |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 124 | if (sys.platform != "win32" and |
| 125 | (self.distribution.has_ext_modules() or |
| 126 | self.distribution.has_c_libraries())): |
Thomas Heller | 904ca11 | 2000-09-07 15:59:22 +0000 | [diff] [blame] | 127 | raise DistutilsPlatformError \ |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 128 | ("distribution contains extensions and/or C libraries; " |
| 129 | "must be compiled on a Windows 32 platform") |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 130 | |
Martin v. Löwis | 9668b93 | 2002-01-12 11:27:42 +0000 | [diff] [blame] | 131 | if not self.skip_build: |
| 132 | self.run_command('build') |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 133 | |
Thomas Heller | cd494ad | 2003-06-12 17:23:58 +0000 | [diff] [blame] | 134 | install = self.reinitialize_command('install', reinit_subcommands=1) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 135 | install.root = self.bdist_dir |
Martin v. Löwis | 9668b93 | 2002-01-12 11:27:42 +0000 | [diff] [blame] | 136 | install.skip_build = self.skip_build |
Thomas Heller | fd0e82a | 2002-04-09 14:14:38 +0000 | [diff] [blame] | 137 | install.warn_dir = 0 |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 138 | install.plat_name = self.plat_name |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 139 | |
Greg Ward | b0b98a5 | 2000-06-28 00:56:20 +0000 | [diff] [blame] | 140 | install_lib = self.reinitialize_command('install_lib') |
Greg Ward | 018cbb1 | 2000-08-26 02:40:10 +0000 | [diff] [blame] | 141 | # we do not want to include pyc or pyo files |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 142 | install_lib.compile = 0 |
| 143 | install_lib.optimize = 0 |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 144 | |
Mark Hammond | 79d9bfa | 2004-10-27 21:54:33 +0000 | [diff] [blame] | 145 | if self.distribution.has_ext_modules(): |
| 146 | # If we are building an installer for a Python version other |
| 147 | # than the one we are currently running, then we need to ensure |
| 148 | # our build_lib reflects the other Python version rather than ours. |
| 149 | # Note that for target_version!=sys.version, we must have skipped the |
| 150 | # build step, so there is no issue with enforcing the build of this |
| 151 | # version. |
| 152 | target_version = self.target_version |
| 153 | if not target_version: |
| 154 | assert self.skip_build, "Should have already checked this" |
Serhiy Storchaka | 885bdc4 | 2016-02-11 13:10:36 +0200 | [diff] [blame] | 155 | target_version = '%d.%d' % sys.version_info[:2] |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 156 | plat_specifier = ".%s-%s" % (self.plat_name, target_version) |
Mark Hammond | 79d9bfa | 2004-10-27 21:54:33 +0000 | [diff] [blame] | 157 | build = self.get_finalized_command('build') |
| 158 | build.build_lib = os.path.join(build.build_base, |
| 159 | 'lib' + plat_specifier) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 160 | |
Thomas Heller | c010c17 | 2001-09-05 13:00:40 +0000 | [diff] [blame] | 161 | # Use a custom scheme for the zip-file, because we have to decide |
| 162 | # at installation time which scheme to use. |
| 163 | for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): |
Neal Norwitz | 9d72bb4 | 2007-04-17 08:48:32 +0000 | [diff] [blame] | 164 | value = key.upper() |
Thomas Heller | c010c17 | 2001-09-05 13:00:40 +0000 | [diff] [blame] | 165 | if key == 'headers': |
| 166 | value = value + '/Include/$dist_name' |
| 167 | setattr(install, |
| 168 | 'install_' + key, |
| 169 | value) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 170 | |
Jeremy Hylton | cd8a114 | 2002-06-04 20:14:43 +0000 | [diff] [blame] | 171 | log.info("installing to %s", self.bdist_dir) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 172 | install.ensure_finalized() |
Thomas Heller | c010c17 | 2001-09-05 13:00:40 +0000 | [diff] [blame] | 173 | |
| 174 | # avoid warning of 'install_lib' about installing |
| 175 | # into a directory not in sys.path |
| 176 | sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) |
| 177 | |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 178 | install.run() |
| 179 | |
Thomas Heller | c010c17 | 2001-09-05 13:00:40 +0000 | [diff] [blame] | 180 | del sys.path[0] |
| 181 | |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 182 | # And make an archive relative to the root of the |
| 183 | # pseudo-installation tree. |
Thomas Heller | d98d25e | 2002-10-15 14:51:58 +0000 | [diff] [blame] | 184 | from tempfile import mktemp |
| 185 | archive_basename = mktemp() |
Greg Ward | fd9f168 | 2000-07-05 03:08:55 +0000 | [diff] [blame] | 186 | fullname = self.distribution.get_fullname() |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 187 | arcname = self.make_archive(archive_basename, "zip", |
Thomas Heller | c010c17 | 2001-09-05 13:00:40 +0000 | [diff] [blame] | 188 | root_dir=self.bdist_dir) |
Thomas Heller | c7cb9ed | 2001-12-18 20:13:40 +0000 | [diff] [blame] | 189 | # create an exe containing the zip-file |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 190 | self.create_exe(arcname, fullname, self.bitmap) |
Martin v. Löwis | 98da562 | 2005-03-23 18:54:36 +0000 | [diff] [blame] | 191 | if self.distribution.has_ext_modules(): |
| 192 | pyversion = get_python_version() |
| 193 | else: |
| 194 | pyversion = 'any' |
| 195 | self.distribution.dist_files.append(('bdist_wininst', pyversion, |
Martin v. Löwis | 24ff83d | 2005-03-22 22:23:29 +0000 | [diff] [blame] | 196 | self.get_installer_filename(fullname))) |
Thomas Heller | c7cb9ed | 2001-12-18 20:13:40 +0000 | [diff] [blame] | 197 | # remove the zip-file again |
Jeremy Hylton | cd8a114 | 2002-06-04 20:14:43 +0000 | [diff] [blame] | 198 | log.debug("removing temporary file '%s'", arcname) |
Thomas Heller | d98d25e | 2002-10-15 14:51:58 +0000 | [diff] [blame] | 199 | os.remove(arcname) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 200 | |
Greg Ward | 3bfc8c8 | 2000-09-16 15:56:32 +0000 | [diff] [blame] | 201 | if not self.keep_temp: |
Jeremy Hylton | cd8a114 | 2002-06-04 20:14:43 +0000 | [diff] [blame] | 202 | remove_tree(self.bdist_dir, dry_run=self.dry_run) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 203 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 204 | def get_inidata(self): |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 205 | # Return data describing the installation. |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 206 | lines = [] |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 207 | metadata = self.distribution.metadata |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 208 | |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 209 | # Write the [metadata] section. |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 210 | lines.append("[metadata]") |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 211 | |
Greg Ward | b0b98a5 | 2000-06-28 00:56:20 +0000 | [diff] [blame] | 212 | # 'info' will be displayed in the installer's dialog box, |
| 213 | # describing the items to be installed. |
Greg Ward | 6f9320b | 2000-08-27 20:44:13 +0000 | [diff] [blame] | 214 | info = (metadata.long_description or '') + '\n' |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 215 | |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 216 | # Escape newline characters |
| 217 | def escape(s): |
Neal Norwitz | 9d72bb4 | 2007-04-17 08:48:32 +0000 | [diff] [blame] | 218 | return s.replace("\n", "\\n") |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 219 | |
Thomas Heller | e138f36 | 2001-10-05 20:40:48 +0000 | [diff] [blame] | 220 | for name in ["author", "author_email", "description", "maintainer", |
| 221 | "maintainer_email", "name", "url", "version"]: |
| 222 | data = getattr(metadata, name, "") |
| 223 | if data: |
| 224 | info = info + ("\n %s: %s" % \ |
Neal Norwitz | 9d72bb4 | 2007-04-17 08:48:32 +0000 | [diff] [blame] | 225 | (name.capitalize(), escape(data))) |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 226 | lines.append("%s=%s" % (name, escape(data))) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 227 | |
| 228 | # The [setup] section contains entries controlling |
| 229 | # the installer runtime. |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 230 | lines.append("\n[Setup]") |
Thomas Heller | d2d58e0 | 2002-02-20 08:01:19 +0000 | [diff] [blame] | 231 | if self.install_script: |
| 232 | lines.append("install_script=%s" % self.install_script) |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 233 | lines.append("info=%s" % escape(info)) |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 234 | lines.append("target_compile=%d" % (not self.no_target_compile)) |
| 235 | lines.append("target_optimize=%d" % (not self.no_target_optimize)) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 236 | if self.target_version: |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 237 | lines.append("target_version=%s" % self.target_version) |
Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 238 | if self.user_access_control: |
| 239 | lines.append("user_access_control=%s" % self.user_access_control) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 240 | |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 241 | title = self.title or self.distribution.get_fullname() |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 242 | lines.append("title=%s" % escape(title)) |
Thomas Heller | ecaf0d8 | 2000-09-09 21:15:12 +0000 | [diff] [blame] | 243 | import time |
| 244 | import distutils |
Thomas Heller | a19cdad | 2004-02-20 14:43:21 +0000 | [diff] [blame] | 245 | build_info = "Built %s with distutils-%s" % \ |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 246 | (time.ctime(time.time()), distutils.__version__) |
| 247 | lines.append("build_info=%s" % build_info) |
Neal Norwitz | 9d72bb4 | 2007-04-17 08:48:32 +0000 | [diff] [blame] | 248 | return "\n".join(lines) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 249 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 250 | def create_exe(self, arcname, fullname, bitmap=None): |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 251 | import struct |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 252 | |
Greg Ward | 1ac9802 | 2000-09-01 01:44:45 +0000 | [diff] [blame] | 253 | self.mkpath(self.dist_dir) |
| 254 | |
| 255 | cfgdata = self.get_inidata() |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 256 | |
Thomas Heller | 612eb09 | 2004-07-23 19:58:28 +0000 | [diff] [blame] | 257 | installer_name = self.get_installer_filename(fullname) |
Greg Ward | cb1f4c4 | 2000-09-30 18:27:54 +0000 | [diff] [blame] | 258 | self.announce("creating %s" % installer_name) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 259 | |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 260 | if bitmap: |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 261 | with open(bitmap, "rb") as f: |
| 262 | bitmapdata = f.read() |
Thomas Heller | e09f639 | 2001-02-19 09:20:30 +0000 | [diff] [blame] | 263 | bitmaplen = len(bitmapdata) |
| 264 | else: |
| 265 | bitmaplen = 0 |
| 266 | |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 267 | with open(installer_name, "wb") as file: |
| 268 | file.write(self.get_exe_bytes()) |
| 269 | if bitmap: |
| 270 | file.write(bitmapdata) |
Fred Drake | 21d4535 | 2001-12-06 21:01:19 +0000 | [diff] [blame] | 271 | |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 272 | # Convert cfgdata from unicode to ascii, mbcs encoded |
| 273 | if isinstance(cfgdata, str): |
| 274 | cfgdata = cfgdata.encode("mbcs") |
Thomas Heller | ee6fd06 | 2004-07-23 19:44:29 +0000 | [diff] [blame] | 275 | |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 276 | # Append the pre-install script |
Antoine Pitrou | 1d6a16b | 2008-09-04 21:32:09 +0000 | [diff] [blame] | 277 | cfgdata = cfgdata + b"\0" |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 278 | if self.pre_install_script: |
| 279 | # We need to normalize newlines, so we open in text mode and |
| 280 | # convert back to bytes. "latin-1" simply avoids any possible |
| 281 | # failures. |
| 282 | with open(self.pre_install_script, "r", |
Inada Naoki | 5909ad1 | 2019-04-09 14:54:30 +0900 | [diff] [blame] | 283 | encoding="latin-1") as script: |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 284 | script_data = script.read().encode("latin-1") |
| 285 | cfgdata = cfgdata + script_data + b"\n\0" |
| 286 | else: |
| 287 | # empty pre-install script |
| 288 | cfgdata = cfgdata + b"\0" |
| 289 | file.write(cfgdata) |
Thomas Heller | b8f134e | 2004-07-19 09:45:46 +0000 | [diff] [blame] | 290 | |
Mickaël Schoentgen | 58721a9 | 2019-04-08 13:08:48 +0000 | [diff] [blame] | 291 | # The 'magic number' 0x1234567B is used to make sure that the |
| 292 | # binary layout of 'cfgdata' is what the wininst.exe binary |
| 293 | # expects. If the layout changes, increment that number, make |
| 294 | # the corresponding changes to the wininst.exe sources, and |
| 295 | # recompile them. |
| 296 | header = struct.pack("<iii", |
| 297 | 0x1234567B, # tag |
| 298 | len(cfgdata), # length |
| 299 | bitmaplen, # number of bytes in bitmap |
| 300 | ) |
| 301 | file.write(header) |
| 302 | with open(arcname, "rb") as f: |
| 303 | file.write(f.read()) |
Greg Ward | 27199e8 | 2000-06-27 01:24:38 +0000 | [diff] [blame] | 304 | |
Thomas Heller | 612eb09 | 2004-07-23 19:58:28 +0000 | [diff] [blame] | 305 | def get_installer_filename(self, fullname): |
| 306 | # Factored out to allow overriding in subclasses |
| 307 | if self.target_version: |
| 308 | # if we create an installer for a specific python version, |
| 309 | # it's better to include this in the name |
| 310 | installer_name = os.path.join(self.dist_dir, |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 311 | "%s.%s-py%s.exe" % |
| 312 | (fullname, self.plat_name, self.target_version)) |
Thomas Heller | 612eb09 | 2004-07-23 19:58:28 +0000 | [diff] [blame] | 313 | else: |
| 314 | installer_name = os.path.join(self.dist_dir, |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 315 | "%s.%s.exe" % (fullname, self.plat_name)) |
Thomas Heller | 30d0008 | 2004-08-17 10:15:07 +0000 | [diff] [blame] | 316 | return installer_name |
Thomas Heller | 612eb09 | 2004-07-23 19:58:28 +0000 | [diff] [blame] | 317 | |
Collin Winter | 5b7e9d7 | 2007-08-30 03:52:21 +0000 | [diff] [blame] | 318 | def get_exe_bytes(self): |
Thomas Heller | a146fea | 2004-07-06 19:23:27 +0000 | [diff] [blame] | 319 | # If a target-version other than the current version has been |
| 320 | # specified, then using the MSVC version from *this* build is no good. |
| 321 | # Without actually finding and executing the target version and parsing |
| 322 | # its sys.version, we just hard-code our knowledge of old versions. |
| 323 | # NOTE: Possible alternative is to allow "--target-version" to |
| 324 | # specify a Python executable rather than a simple version string. |
| 325 | # We can then execute this program to obtain any info we need, such |
| 326 | # as the real sys.version string for the build. |
| 327 | cur_version = get_python_version() |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 328 | |
| 329 | # If the target version is *later* than us, then we assume they |
| 330 | # use what we use |
| 331 | # string compares seem wrong, but are what sysconfig.py itself uses |
| 332 | if self.target_version and self.target_version < cur_version: |
| 333 | if self.target_version < "2.4": |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 334 | bv = '6.0' |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 335 | elif self.target_version == "2.4": |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 336 | bv = '7.1' |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 337 | elif self.target_version == "2.5": |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 338 | bv = '8.0' |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 339 | elif self.target_version <= "3.2": |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 340 | bv = '9.0' |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 341 | elif self.target_version <= "3.4": |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 342 | bv = '10.0' |
Thomas Heller | a146fea | 2004-07-06 19:23:27 +0000 | [diff] [blame] | 343 | else: |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 344 | bv = '14.0' |
Thomas Heller | a146fea | 2004-07-06 19:23:27 +0000 | [diff] [blame] | 345 | else: |
| 346 | # for current version - use authoritative check. |
Steve Dower | c709085 | 2015-05-23 12:15:57 -0700 | [diff] [blame] | 347 | try: |
| 348 | from msvcrt import CRT_ASSEMBLY_VERSION |
| 349 | except ImportError: |
| 350 | # cross-building, so assume the latest version |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 351 | bv = '14.0' |
Steve Dower | c709085 | 2015-05-23 12:15:57 -0700 | [diff] [blame] | 352 | else: |
Steve Dower | 2507e29 | 2018-01-19 09:09:36 +1100 | [diff] [blame] | 353 | # as far as we know, CRT is binary compatible based on |
| 354 | # the first field, so assume 'x.0' until proven otherwise |
| 355 | major = CRT_ASSEMBLY_VERSION.partition('.')[0] |
| 356 | bv = major + '.0' |
Steve Dower | fd3664b | 2015-05-23 09:02:50 -0700 | [diff] [blame] | 357 | |
Thomas Heller | a146fea | 2004-07-06 19:23:27 +0000 | [diff] [blame] | 358 | |
Thomas Heller | 0bc9c91 | 2004-02-20 19:38:50 +0000 | [diff] [blame] | 359 | # wininst-x.y.exe is in the same directory as this file |
Thomas Heller | 450cafa | 2002-11-22 21:08:34 +0000 | [diff] [blame] | 360 | directory = os.path.dirname(__file__) |
Thomas Heller | 0bc9c91 | 2004-02-20 19:38:50 +0000 | [diff] [blame] | 361 | # we must use a wininst-x.y.exe built with the same C compiler |
| 362 | # used for python. XXX What about mingw, borland, and so on? |
Tarek Ziadé | a619180 | 2009-04-09 22:02:39 +0000 | [diff] [blame] | 363 | |
| 364 | # if plat_name starts with "win" but is not "win32" |
| 365 | # we want to strip "win" and leave the rest (e.g. -amd64) |
| 366 | # for all other cases, we don't want any suffix |
| 367 | if self.plat_name != 'win32' and self.plat_name[:3] == 'win': |
| 368 | sfix = self.plat_name[3:] |
Christian Heimes | 5e69685 | 2008-04-09 08:37:03 +0000 | [diff] [blame] | 369 | else: |
Tarek Ziadé | a619180 | 2009-04-09 22:02:39 +0000 | [diff] [blame] | 370 | sfix = '' |
| 371 | |
Steve Dower | 5fcd5e6 | 2017-09-06 10:01:38 -0700 | [diff] [blame] | 372 | filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix)) |
Éric Araujo | bee5cef | 2010-11-05 23:51:56 +0000 | [diff] [blame] | 373 | f = open(filename, "rb") |
| 374 | try: |
| 375 | return f.read() |
| 376 | finally: |
| 377 | f.close() |