[bumpversion]
-current_version = 64.0.3
+current_version = 65.0.0
commit = True
tag = True
+v65.0.0
+-------
+
+
+Breaking Changes
+^^^^^^^^^^^^^^^^
+* #3505: Removed 'msvccompiler' and 'msvc9compiler' modules from distutils.
+* #3521: Remove bdist_msi and bdist_wininst commands, which have been deprecated since Python 3.9. Use older Setuptools for these behaviors if needed.
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #3519: Changed the note in ``keywords`` documentation regarding editable installations
+ to specify which ``setuptools`` version require a minimal ``setup.py`` file or not.
+
+
v64.0.3
-------
:synopsis: Build a "dumb" installer - a simple archive of files
-.. % todo
-
-
-:mod:`distutils.command.bdist_msi` --- Build a Microsoft Installer binary package
-=================================================================================
-
-.. module:: distutils.command.bdist_msi
- :synopsis: Build a binary distribution as a Windows MSI file
-
-.. class:: bdist_msi
-
-.. deprecated:: 3.9
- Use bdist_wheel (wheel packages) instead.
-
- Builds a `Windows Installer`_ (.msi) binary package.
-
- .. _Windows Installer: https://msdn.microsoft.com/en-us/library/cc185688(VS.85).aspx
-
- In most cases, the ``bdist_msi`` installer is a better choice than the
- ``bdist_wininst`` installer, because it provides better support for
- Win64 platforms, allows administrators to perform non-interactive
- installations, and allows installation through group policies.
-
-
:mod:`distutils.command.bdist_rpm` --- Build a binary distribution as a Redhat RPM and SRPM
===========================================================================================
.. % todo
-:mod:`distutils.command.bdist_wininst` --- Build a Windows installer
-====================================================================
-
-.. module:: distutils.command.bdist_wininst
- :synopsis: Build a Windows installer
-
-.. deprecated:: 3.8
- Use bdist_wheel (wheel packages) instead.
-
-
-.. % todo
-
-
:mod:`distutils.command.sdist` --- Build a source distribution
==============================================================
+-------------+------------------------------+---------+
| ``sdux`` | HP-UX :program:`swinstall` | |
+-------------+------------------------------+---------+
-| ``wininst`` | self-extracting ZIP file for | \(4) |
-| | Windows | |
-+-------------+------------------------------+---------+
-| ``msi`` | Microsoft Installer. | |
-+-------------+------------------------------+---------+
.. versionchanged:: 3.5
Added support for the ``xztar`` format.
+--------------------------+-------------------------------------+
| :command:`bdist_rpm` | rpm, srpm |
+--------------------------+-------------------------------------+
-| :command:`bdist_wininst` | wininst |
-+--------------------------+-------------------------------------+
-| :command:`bdist_msi` | msi |
-+--------------------------+-------------------------------------+
-
-.. note::
- bdist_wininst is deprecated since Python 3.8.
-
-.. note::
- bdist_msi is deprecated since Python 3.9.
The following sections give details on the individual :command:`bdist_\*`
commands.
explicitly specify multiple :command:`bdist_\*` commands and their options::
python setup.py bdist_rpm --packager="John Doe <jdoe@example.org>" \
- bdist_wininst --target-version="2.0"
+ bdist_dumb --dumb-option=foo
Creating RPM packages is driven by a :file:`.spec` file, much as using the
Distutils is driven by the setup script. To make your life easier, the
.. % \command{bdist\_rpm} command with one that writes whatever else you want
.. % to the \file{.spec} file.)
-
-.. _creating-wininst:
-
-Creating Windows Installers
-===========================
-
-.. warning::
- bdist_wininst is deprecated since Python 3.8.
-
-.. warning::
- bdist_msi is deprecated since Python 3.9.
-
-Executable installers are the natural format for binary distributions on
-Windows. They display a nice graphical user interface, display some information
-about the module distribution to be installed taken from the metadata in the
-setup script, let the user select a few options, and start or cancel the
-installation.
-
-Since the metadata is taken from the setup script, creating Windows installers
-is usually as easy as running::
-
- python setup.py bdist_wininst
-
-or the :command:`bdist` command with the :option:`!--formats` option::
-
- python setup.py bdist --formats=wininst
-
-If you have a pure module distribution (only containing pure Python modules and
-packages), the resulting installer will be version independent and have a name
-like :file:`foo-1.0.win32.exe`. Note that creating ``wininst`` binary
-distributions in only supported on Windows systems.
-
-If you have a non-pure distribution, the extensions can only be created on a
-Windows platform, and will be Python version dependent. The installer filename
-will reflect this and now has the form :file:`foo-1.0.win32-py2.0.exe`. You
-have to create a separate installer for every Python version you want to
-support.
-
-The installer will try to compile pure modules into :term:`bytecode` after installation
-on the target system in normal and optimizing mode. If you don't want this to
-happen for some reason, you can run the :command:`bdist_wininst` command with
-the :option:`!--no-target-compile` and/or the :option:`!--no-target-optimize`
-option.
-
-By default the installer will display the cool "Python Powered" logo when it is
-run, but you can also supply your own 152x261 bitmap which must be a Windows
-:file:`.bmp` file with the :option:`!--bitmap` option.
-
-The installer will also display a large title on the desktop background window
-when it is run, which is constructed from the name of your distribution and the
-version number. This can be changed to another text by using the
-:option:`!--title` option.
-
-The installer file will be written to the "distribution directory" --- normally
-:file:`dist/`, but customizable with the :option:`!--dist-dir` option.
-
.. _cross-compile-windows:
Cross-compiling on Windows
python setup.py build --plat-name=win-amd64
-to build a 64bit version of your extension. The Windows Installers also
-support this option, so the command::
-
- python setup.py build --plat-name=win-amd64 bdist_wininst
-
-would create a 64bit installation executable on your 32bit version of Windows.
+to build a 64bit version of your extension.
To cross-compile, you must download the Python source code and cross-compile
Python itself for the platform you are targeting - it is not possible from a
and *iconindex* is the index of the icon in the file *iconpath*. Again, for
details consult the Microsoft documentation for the :class:`IShellLink`
interface.
-
-
-Vista User Access Control (UAC)
-===============================
-
-Starting with Python 2.6, bdist_wininst supports a :option:`!--user-access-control`
-option. The default is 'none' (meaning no UAC handling is done), and other
-valid values are 'auto' (meaning prompt for UAC elevation if Python was
-installed for all users) and 'force' (meaning always prompt for elevation).
-
-.. note::
- bdist_wininst is deprecated since Python 3.8.
-
-.. note::
- bdist_msi is deprecated since Python 3.9.
.. % \subsection{\protect\command{bdist}}
.. % \subsection{\protect\command{bdist\_dumb}}
.. % \subsection{\protect\command{bdist\_rpm}}
-.. % \subsection{\protect\command{bdist\_wininst}}
:command:`install` is more often for installers (although most developers will
want to install their own code occasionally).
-If you want to make things really easy for your users, you can create one or
-more built distributions for them. For instance, if you are running on a
-Windows machine, and want to make things easy for other Windows users, you can
-create an executable installer (the most appropriate type of built distribution
-for this platform) with the :command:`bdist_wininst` command. For example::
-
- python setup.py bdist_wininst
-
-will create an executable installer, :file:`foo-1.0.win32.exe`, in the current
-directory.
-
Other useful built distribution formats are RPM, implemented by the
:command:`bdist_rpm` command, Solaris :program:`pkgtool`
(:command:`bdist_pkgtool`), and HP-UX :program:`swinstall`
extensions).
.. note::
- When using declarative configs via ``pyproject.toml`` users can still keep a
- very simple ``setup.py`` just to ensure editable installs are supported, for
- example::
+ When using declarative configs via ``pyproject.toml``
+ with ``setuptools<64.0.0``, users can still keep a very simple ``setup.py``
+ just to ensure editable installs are supported, for example::
from setuptools import setup
setup()
- Future versions of ``setuptools`` may support editable installs even
- without ``setup.py``.
-
+ Versions of ``setuptools`` ``>=64.0.0`` do not require this extra minimal
+ ``setup.py`` file.
.. _keyword/name:
Users are encouraged to use tools like :mod:`importlib.resources` or
:mod:`importlib.metadata` when trying to access package files directly.
- Editable installations may not work with
- :doc:`namespaces created with pkgutil or pkg_resouces
+ :doc:`namespaces created with pkgutil or pkg_resources
<PyPUG:guides/packaging-namespace-packages>`.
Please use :pep:`420`-style implicit namespaces [#namespaces]_.
- Support for :pep:`420`-style implicit namespace packages for
[metadata]
name = setuptools
-version = 64.0.3
+version = 65.0.0
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
import sys
import os
import re
+import warnings
+
from distutils.errors import (
CompileError,
LinkError,
obj_names = []
for src_name in source_filenames:
base, ext = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
+ base = self._mangle_base(base)
if ext not in self.src_extensions:
raise UnknownFileError(
"unknown file type '{}' (from '{}')".format(ext, src_name)
obj_names.append(os.path.join(output_dir, base + self.obj_extension))
return obj_names
+ @staticmethod
+ def _mangle_base(base):
+ """
+ For unknown reasons, absolute paths are mangled.
+ """
+ # Chop off the drive
+ no_drive = os.path.splitdrive(base)[1]
+ # If abs, chop off leading /
+ rel = no_drive[os.path.isabs(no_drive) :]
+ if rel != base:
+ msg = (
+ f"Absolute path {base!r} is being replaced with a "
+ f"relative path {rel!r} for outputs. This behavior is "
+ "deprecated. If this behavior is desired, please "
+ "comment in pypa/distutils#169."
+ )
+ warnings.warn(msg, DeprecationWarning)
+ return rel
+
def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
assert output_dir is not None
if strip_dir:
'bdist',
'bdist_dumb',
'bdist_rpm',
- 'bdist_wininst',
'check',
'upload',
]
'xztar': ('bdist_dumb', "xz'ed tar file"),
'ztar': ('bdist_dumb', "compressed tar file"),
'tar': ('bdist_dumb', "tar file"),
- 'wininst': ('bdist_wininst', "Windows executable installer"),
'zip': ('bdist_dumb', "ZIP file"),
- 'msi': ('bdist_msi', "Microsoft Installer"),
}
)
+++ /dev/null
-# Copyright (C) 2005, 2006 Martin von Löwis
-# Licensed to PSF under a Contributor Agreement.
-# The bdist_wininst command proper
-# based on bdist_wininst
-"""
-Implements the bdist_msi command.
-"""
-
-import os
-import sys
-import warnings
-from distutils.core import Command
-from distutils.dir_util import remove_tree
-from distutils.sysconfig import get_python_version
-from distutils.version import StrictVersion
-from distutils.errors import DistutilsOptionError
-from distutils.util import get_platform
-from distutils import log
-import msilib
-from msilib import schema, sequence, text
-from msilib import Directory, Feature, Dialog, add_data
-
-
-class PyDialog(Dialog):
- """Dialog class with a fixed layout: controls at the top, then a ruler,
- then a list of buttons: back, next, cancel. Optionally a bitmap at the
- left."""
-
- def __init__(self, *args, **kw):
- """Dialog(database, name, x, y, w, h, attributes, title, first,
- default, cancel, bitmap=true)"""
- super().__init__(*args)
- ruler = self.h - 36
- self.line("BottomLine", 0, ruler, self.w, 0)
-
- def title(self, title):
- "Set the title text of the dialog at the top."
- # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix,
- # text, in VerdanaBold10
- self.text("Title", 15, 10, 320, 60, 0x30003, r"{\VerdanaBold10}%s" % title)
-
- def back(self, title, next, name="Back", active=1):
- """Add a back button with a given title, the tab-next button,
- its name in the Control table, possibly initially disabled.
-
- Return the button, so that events can be associated"""
- if active:
- flags = 3 # Visible|Enabled
- else:
- flags = 1 # Visible
- return self.pushbutton(name, 180, self.h - 27, 56, 17, flags, title, next)
-
- def cancel(self, title, next, name="Cancel", active=1):
- """Add a cancel button with a given title, the tab-next button,
- its name in the Control table, possibly initially disabled.
-
- Return the button, so that events can be associated"""
- if active:
- flags = 3 # Visible|Enabled
- else:
- flags = 1 # Visible
- return self.pushbutton(name, 304, self.h - 27, 56, 17, flags, title, next)
-
- def next(self, title, next, name="Next", active=1):
- """Add a Next button with a given title, the tab-next button,
- its name in the Control table, possibly initially disabled.
-
- Return the button, so that events can be associated"""
- if active:
- flags = 3 # Visible|Enabled
- else:
- flags = 1 # Visible
- return self.pushbutton(name, 236, self.h - 27, 56, 17, flags, title, next)
-
- def xbutton(self, name, title, next, xpos):
- """Add a button with a given title, the tab-next button,
- its name in the Control table, giving its x position; the
- y-position is aligned with the other buttons.
-
- Return the button, so that events can be associated"""
- return self.pushbutton(
- name, int(self.w * xpos - 28), self.h - 27, 56, 17, 3, title, next
- )
-
-
-class bdist_msi(Command):
-
- description = "create a Microsoft Installer (.msi) binary distribution"
-
- user_options = [
- ('bdist-dir=', None, "temporary directory for creating the distribution"),
- (
- 'plat-name=',
- 'p',
- "platform name to embed in generated filenames "
- "(default: %s)" % get_platform(),
- ),
- (
- 'keep-temp',
- 'k',
- "keep the pseudo-installation tree around after "
- + "creating the distribution archive",
- ),
- (
- 'target-version=',
- None,
- "require a specific python version" + " on the target system",
- ),
- ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"),
- (
- 'no-target-optimize',
- 'o',
- "do not compile .py to .pyo (optimized) " "on the target system",
- ),
- ('dist-dir=', 'd', "directory to put final built distributions in"),
- ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
- (
- 'install-script=',
- None,
- "basename of installation script to be run after "
- "installation or before deinstallation",
- ),
- (
- 'pre-install-script=',
- None,
- "Fully qualified filename of a script to be run before "
- "any files are installed. This script need not be in the "
- "distribution",
- ),
- ]
-
- boolean_options = [
- 'keep-temp',
- 'no-target-compile',
- 'no-target-optimize',
- 'skip-build',
- ]
-
- all_versions = [
- '2.0',
- '2.1',
- '2.2',
- '2.3',
- '2.4',
- '2.5',
- '2.6',
- '2.7',
- '2.8',
- '2.9',
- '3.0',
- '3.1',
- '3.2',
- '3.3',
- '3.4',
- '3.5',
- '3.6',
- '3.7',
- '3.8',
- '3.9',
- ]
- other_version = 'X'
-
- def __init__(self, *args, **kw):
- super().__init__(*args, **kw)
- warnings.warn(
- "bdist_msi command is deprecated since Python 3.9, "
- "use bdist_wheel (wheel packages) instead",
- DeprecationWarning,
- 2,
- )
-
- def initialize_options(self):
- self.bdist_dir = None
- self.plat_name = None
- self.keep_temp = 0
- self.no_target_compile = 0
- self.no_target_optimize = 0
- self.target_version = None
- self.dist_dir = None
- self.skip_build = None
- self.install_script = None
- self.pre_install_script = None
- self.versions = None
-
- def finalize_options(self):
- self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
-
- if self.bdist_dir is None:
- bdist_base = self.get_finalized_command('bdist').bdist_base
- self.bdist_dir = os.path.join(bdist_base, 'msi')
-
- short_version = get_python_version()
- if (not self.target_version) and self.distribution.has_ext_modules():
- self.target_version = short_version
-
- if self.target_version:
- self.versions = [self.target_version]
- if (
- not self.skip_build
- and self.distribution.has_ext_modules()
- and self.target_version != short_version
- ):
- raise DistutilsOptionError(
- "target version can only be %s, or the '--skip-build'"
- " option must be specified" % (short_version,)
- )
- else:
- self.versions = list(self.all_versions)
-
- self.set_undefined_options(
- 'bdist',
- ('dist_dir', 'dist_dir'),
- ('plat_name', 'plat_name'),
- )
-
- if self.pre_install_script:
- raise DistutilsOptionError(
- "the pre-install-script feature is not yet implemented"
- )
-
- if self.install_script:
- for script in self.distribution.scripts:
- if self.install_script == os.path.basename(script):
- break
- else:
- raise DistutilsOptionError(
- "install_script '%s' not found in scripts" % self.install_script
- )
- self.install_script_key = None
-
- def run(self): # noqa: C901
- if not self.skip_build:
- self.run_command('build')
-
- install = self.reinitialize_command('install', reinit_subcommands=1)
- install.prefix = self.bdist_dir
- install.skip_build = self.skip_build
- install.warn_dir = 0
-
- install_lib = self.reinitialize_command('install_lib')
- # we do not want to include pyc or pyo files
- install_lib.compile = 0
- install_lib.optimize = 0
-
- if self.distribution.has_ext_modules():
- # If we are building an installer for a Python version other
- # than the one we are currently running, then we need to ensure
- # our build_lib reflects the other Python version rather than ours.
- # Note that for target_version!=sys.version, we must have skipped the
- # build step, so there is no issue with enforcing the build of this
- # version.
- target_version = self.target_version
- if not target_version:
- assert self.skip_build, "Should have already checked this"
- target_version = '%d.%d' % sys.version_info[:2]
- plat_specifier = ".{}-{}".format(self.plat_name, target_version)
- build = self.get_finalized_command('build')
- build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier)
-
- log.info("installing to %s", self.bdist_dir)
- install.ensure_finalized()
-
- # avoid warning of 'install_lib' about installing
- # into a directory not in sys.path
- sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
-
- install.run()
-
- del sys.path[0]
-
- self.mkpath(self.dist_dir)
- fullname = self.distribution.get_fullname()
- installer_name = self.get_installer_filename(fullname)
- installer_name = os.path.abspath(installer_name)
- if os.path.exists(installer_name):
- os.unlink(installer_name)
-
- metadata = self.distribution.metadata
- author = metadata.author or metadata.maintainer
- version = metadata.get_version()
- # ProductVersion must be strictly numeric
- # XXX need to deal with prerelease versions
- sversion = "%d.%d.%d" % StrictVersion(version).version
- # Prefix ProductName with Python x.y, so that
- # it sorts together with the other Python packages
- # in Add-Remove-Programs (APR)
- fullname = self.distribution.get_fullname()
- if self.target_version:
- product_name = "Python {} {}".format(self.target_version, fullname)
- else:
- product_name = "Python %s" % (fullname)
- self.db = msilib.init_database(
- installer_name, schema, product_name, msilib.gen_uuid(), sversion, author
- )
- msilib.add_tables(self.db, sequence)
- props = [('DistVersion', version)]
- email = metadata.author_email or metadata.maintainer_email
- if email:
- props.append(("ARPCONTACT", email))
- if metadata.url:
- props.append(("ARPURLINFOABOUT", metadata.url))
- if props:
- add_data(self.db, 'Property', props)
-
- self.add_find_python()
- self.add_files()
- self.add_scripts()
- self.add_ui()
- self.db.Commit()
-
- if hasattr(self.distribution, 'dist_files'):
- tup = 'bdist_msi', self.target_version or 'any', fullname
- self.distribution.dist_files.append(tup)
-
- if not self.keep_temp:
- remove_tree(self.bdist_dir, dry_run=self.dry_run)
-
- def add_files(self): # noqa: C901
- db = self.db
- cab = msilib.CAB("distfiles")
- rootdir = os.path.abspath(self.bdist_dir)
-
- root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir")
- f = Feature(db, "Python", "Python", "Everything", 0, 1, directory="TARGETDIR")
-
- items = [(f, root, '')]
- for version in self.versions + [self.other_version]:
- target = "TARGETDIR" + version
- name = default = "Python" + version
- desc = "Everything"
- if version is self.other_version:
- title = "Python from another location"
- level = 2
- else:
- title = "Python %s from registry" % version
- level = 1
- f = Feature(db, name, title, desc, 1, level, directory=target)
- dir = Directory(db, cab, root, rootdir, target, default)
- items.append((f, dir, version))
- db.Commit()
-
- seen = {}
- for feature, dir, version in items:
- todo = [dir]
- while todo:
- dir = todo.pop()
- for file in os.listdir(dir.absolute):
- afile = os.path.join(dir.absolute, file)
- if os.path.isdir(afile):
- short = "{}|{}".format(dir.make_short(file), file)
- default = file + version
- newdir = Directory(db, cab, dir, file, default, short)
- todo.append(newdir)
- else:
- if not dir.component:
- dir.start_component(dir.logical, feature, 0)
- if afile not in seen:
- key = seen[afile] = dir.add_file(file)
- if file == self.install_script:
- if self.install_script_key:
- raise DistutilsOptionError(
- "Multiple files with name %s" % file
- )
- self.install_script_key = '[#%s]' % key
- else:
- key = seen[afile]
- add_data(
- self.db,
- "DuplicateFile",
- [
- (
- key + version,
- dir.component,
- key,
- None,
- dir.logical,
- )
- ],
- )
- db.Commit()
- cab.commit(db)
-
- def add_find_python(self):
- """Adds code to the installer to compute the location of Python.
-
- Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the
- registry for each version of Python.
-
- Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined,
- else from PYTHON.MACHINE.X.Y.
-
- Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe"""
-
- start = 402
- for ver in self.versions:
- install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver
- machine_reg = "python.machine." + ver
- user_reg = "python.user." + ver
- machine_prop = "PYTHON.MACHINE." + ver
- user_prop = "PYTHON.USER." + ver
- machine_action = "PythonFromMachine" + ver
- user_action = "PythonFromUser" + ver
- exe_action = "PythonExe" + ver
- target_dir_prop = "TARGETDIR" + ver
- exe_prop = "PYTHON" + ver
-
- # Type: msidbLocatorTypeRawValue + msidbLocatorType64bit
- Type = 2 + 16 * bool(msilib.Win64)
- add_data(
- self.db,
- "RegLocator",
- [
- (machine_reg, 2, install_path, None, Type),
- (user_reg, 1, install_path, None, Type),
- ],
- )
- add_data(
- self.db,
- "AppSearch",
- [(machine_prop, machine_reg), (user_prop, user_reg)],
- )
- add_data(
- self.db,
- "CustomAction",
- [
- (
- machine_action,
- 51 + 256,
- target_dir_prop,
- "[" + machine_prop + "]",
- ),
- (user_action, 51 + 256, target_dir_prop, "[" + user_prop + "]"),
- (
- exe_action,
- 51 + 256,
- exe_prop,
- "[" + target_dir_prop + "]\\python.exe",
- ),
- ],
- )
- add_data(
- self.db,
- "InstallExecuteSequence",
- [
- (machine_action, machine_prop, start),
- (user_action, user_prop, start + 1),
- (exe_action, None, start + 2),
- ],
- )
- add_data(
- self.db,
- "InstallUISequence",
- [
- (machine_action, machine_prop, start),
- (user_action, user_prop, start + 1),
- (exe_action, None, start + 2),
- ],
- )
- add_data(self.db, "Condition", [("Python" + ver, 0, "NOT TARGETDIR" + ver)])
- start += 4
- assert start < 500
-
- def add_scripts(self):
- if self.install_script:
- start = 6800
- for ver in self.versions + [self.other_version]:
- install_action = "install_script." + ver
- exe_prop = "PYTHON" + ver
- add_data(
- self.db,
- "CustomAction",
- [(install_action, 50, exe_prop, self.install_script_key)],
- )
- add_data(
- self.db,
- "InstallExecuteSequence",
- [(install_action, "&Python%s=3" % ver, start)],
- )
- start += 1
- # XXX pre-install scripts are currently refused in finalize_options()
- # but if this feature is completed, it will also need to add
- # entries for each version as the above code does
- if self.pre_install_script:
- scriptfn = os.path.join(self.bdist_dir, "preinstall.bat")
- with open(scriptfn, "w") as f:
- # The batch file will be executed with [PYTHON], so that %1
- # is the path to the Python interpreter; %0 will be the path
- # of the batch file.
- # rem ="""
- # %1 %0
- # exit
- # """
- # <actual script>
- f.write('rem ="""\n%1 %0\nexit\n"""\n')
- with open(self.pre_install_script) as fin:
- f.write(fin.read())
- add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn))])
- add_data(self.db, "CustomAction", [("PreInstall", 2, "PreInstall", None)])
- add_data(
- self.db,
- "InstallExecuteSequence",
- [("PreInstall", "NOT Installed", 450)],
- )
-
- def add_ui(self):
- db = self.db
- x = y = 50
- w = 370
- h = 300
- title = "[ProductName] Setup"
-
- # see "Dialog Style Bits"
- modal = 3 # visible | modal
- modeless = 1 # visible
-
- # UI customization properties
- add_data(
- db,
- "Property",
- # See "DefaultUIFont Property"
- [
- ("DefaultUIFont", "DlgFont8"),
- # See "ErrorDialog Style Bit"
- ("ErrorDialog", "ErrorDlg"),
- ("Progress1", "Install"), # modified in maintenance type dlg
- ("Progress2", "installs"),
- ("MaintenanceForm_Action", "Repair"),
- # possible values: ALL, JUSTME
- ("WhichUsers", "ALL"),
- ],
- )
-
- # Fonts, see "TextStyle Table"
- add_data(
- db,
- "TextStyle",
- [
- ("DlgFont8", "Tahoma", 9, None, 0),
- ("DlgFontBold8", "Tahoma", 8, None, 1), # bold
- ("VerdanaBold10", "Verdana", 10, None, 1),
- ("VerdanaRed9", "Verdana", 9, 255, 0),
- ],
- )
-
- # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table"
- # Numbers indicate sequence; see sequence.py for how these action integrate
- add_data(
- db,
- "InstallUISequence",
- [
- ("PrepareDlg", "Not Privileged or Windows9x or Installed", 140),
- (
- "WhichUsersDlg",
- "Privileged and not Windows9x and not Installed",
- 141,
- ),
- # In the user interface, assume all-users installation if privileged.
- ("SelectFeaturesDlg", "Not Installed", 1230),
- # XXX no support for resume installations yet
- # ("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240),
- (
- "MaintenanceTypeDlg",
- "Installed AND NOT RESUME AND NOT Preselected",
- 1250,
- ),
- ("ProgressDlg", None, 1280),
- ],
- )
-
- add_data(db, 'ActionText', text.ActionText)
- add_data(db, 'UIText', text.UIText)
- #####################################################################
- # Standard dialogs: FatalError, UserExit, ExitDialog
- fatal = PyDialog(
- db, "FatalError", x, y, w, h, modal, title, "Finish", "Finish", "Finish"
- )
- fatal.title("[ProductName] Installer ended prematurely")
- fatal.back("< Back", "Finish", active=0)
- fatal.cancel("Cancel", "Back", active=0)
- fatal.text(
- "Description1",
- 15,
- 70,
- 320,
- 80,
- 0x30003,
- "[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.",
- )
- fatal.text(
- "Description2",
- 15,
- 155,
- 320,
- 20,
- 0x30003,
- "Click the Finish button to exit the Installer.",
- )
- c = fatal.next("Finish", "Cancel", name="Finish")
- c.event("EndDialog", "Exit")
-
- user_exit = PyDialog(
- db, "UserExit", x, y, w, h, modal, title, "Finish", "Finish", "Finish"
- )
- user_exit.title("[ProductName] Installer was interrupted")
- user_exit.back("< Back", "Finish", active=0)
- user_exit.cancel("Cancel", "Back", active=0)
- user_exit.text(
- "Description1",
- 15,
- 70,
- 320,
- 80,
- 0x30003,
- "[ProductName] setup was interrupted. Your system has not been modified. "
- "To install this program at a later time, please run the installation "
- "again.",
- )
- user_exit.text(
- "Description2",
- 15,
- 155,
- 320,
- 20,
- 0x30003,
- "Click the Finish button to exit the Installer.",
- )
- c = user_exit.next("Finish", "Cancel", name="Finish")
- c.event("EndDialog", "Exit")
-
- exit_dialog = PyDialog(
- db, "ExitDialog", x, y, w, h, modal, title, "Finish", "Finish", "Finish"
- )
- exit_dialog.title("Completing the [ProductName] Installer")
- exit_dialog.back("< Back", "Finish", active=0)
- exit_dialog.cancel("Cancel", "Back", active=0)
- exit_dialog.text(
- "Description",
- 15,
- 235,
- 320,
- 20,
- 0x30003,
- "Click the Finish button to exit the Installer.",
- )
- c = exit_dialog.next("Finish", "Cancel", name="Finish")
- c.event("EndDialog", "Return")
-
- #####################################################################
- # Required dialog: FilesInUse, ErrorDlg
- inuse = PyDialog(
- db,
- "FilesInUse",
- x,
- y,
- w,
- h,
- 19, # KeepModeless|Modal|Visible
- title,
- "Retry",
- "Retry",
- "Retry",
- bitmap=False,
- )
- inuse.text("Title", 15, 6, 200, 15, 0x30003, r"{\DlgFontBold8}Files in Use")
- inuse.text(
- "Description",
- 20,
- 23,
- 280,
- 20,
- 0x30003,
- "Some files that need to be updated are currently in use.",
- )
- inuse.text(
- "Text",
- 20,
- 55,
- 330,
- 50,
- 3,
- "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.",
- )
- inuse.control(
- "List",
- "ListBox",
- 20,
- 107,
- 330,
- 130,
- 7,
- "FileInUseProcess",
- None,
- None,
- None,
- )
- c = inuse.back("Exit", "Ignore", name="Exit")
- c.event("EndDialog", "Exit")
- c = inuse.next("Ignore", "Retry", name="Ignore")
- c.event("EndDialog", "Ignore")
- c = inuse.cancel("Retry", "Exit", name="Retry")
- c.event("EndDialog", "Retry")
-
- # See "Error Dialog". See "ICE20" for the required names of the controls.
- error = Dialog(
- db,
- "ErrorDlg",
- 50,
- 10,
- 330,
- 101,
- 65543, # Error|Minimize|Modal|Visible
- title,
- "ErrorText",
- None,
- None,
- )
- error.text("ErrorText", 50, 9, 280, 48, 3, "")
- error.pushbutton("N", 120, 72, 81, 21, 3, "No", None).event(
- "EndDialog", "ErrorNo"
- )
- error.pushbutton("Y", 240, 72, 81, 21, 3, "Yes", None).event(
- "EndDialog", "ErrorYes"
- )
- error.pushbutton("A", 0, 72, 81, 21, 3, "Abort", None).event(
- "EndDialog", "ErrorAbort"
- )
- error.pushbutton("C", 42, 72, 81, 21, 3, "Cancel", None).event(
- "EndDialog", "ErrorCancel"
- )
- error.pushbutton("I", 81, 72, 81, 21, 3, "Ignore", None).event(
- "EndDialog", "ErrorIgnore"
- )
- error.pushbutton("O", 159, 72, 81, 21, 3, "Ok", None).event(
- "EndDialog", "ErrorOk"
- )
- error.pushbutton("R", 198, 72, 81, 21, 3, "Retry", None).event(
- "EndDialog", "ErrorRetry"
- )
-
- #####################################################################
- # Global "Query Cancel" dialog
- cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, "No", "No", "No")
- cancel.text(
- "Text",
- 48,
- 15,
- 194,
- 30,
- 3,
- "Are you sure you want to cancel [ProductName] installation?",
- )
- # cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None,
- # "py.ico", None, None)
- c = cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
- c.event("EndDialog", "Exit")
-
- c = cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
- c.event("EndDialog", "Return")
-
- #####################################################################
- # Global "Wait for costing" dialog
- costing = Dialog(
- db,
- "WaitForCostingDlg",
- 50,
- 10,
- 260,
- 85,
- modal,
- title,
- "Return",
- "Return",
- "Return",
- )
- costing.text(
- "Text",
- 48,
- 15,
- 194,
- 30,
- 3,
- "Please wait while the installer finishes determining your disk space "
- "requirements.",
- )
- c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None)
- c.event("EndDialog", "Exit")
-
- #####################################################################
- # Preparation dialog: no user input except cancellation
- prep = PyDialog(
- db, "PrepareDlg", x, y, w, h, modeless, title, "Cancel", "Cancel", "Cancel"
- )
- prep.text(
- "Description",
- 15,
- 70,
- 320,
- 40,
- 0x30003,
- "Please wait while the Installer prepares to guide you through the "
- "installation.",
- )
- prep.title("Welcome to the [ProductName] Installer")
- c = prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...")
- c.mapping("ActionText", "Text")
- c = prep.text("ActionData", 15, 135, 320, 30, 0x30003, None)
- c.mapping("ActionData", "Text")
- prep.back("Back", None, active=0)
- prep.next("Next", None, active=0)
- c = prep.cancel("Cancel", None)
- c.event("SpawnDialog", "CancelDlg")
-
- #####################################################################
- # Feature (Python directory) selection
- seldlg = PyDialog(
- db, "SelectFeaturesDlg", x, y, w, h, modal, title, "Next", "Next", "Cancel"
- )
- seldlg.title("Select Python Installations")
-
- seldlg.text(
- "Hint",
- 15,
- 30,
- 300,
- 20,
- 3,
- "Select the Python locations where %s should be installed."
- % self.distribution.get_fullname(),
- )
-
- seldlg.back("< Back", None, active=0)
- c = seldlg.next("Next >", "Cancel")
- order = 1
- c.event("[TARGETDIR]", "[SourceDir]", ordering=order)
- for version in self.versions + [self.other_version]:
- order += 1
- c.event(
- "[TARGETDIR]",
- "[TARGETDIR%s]" % version,
- "FEATURE_SELECTED AND &Python%s=3" % version,
- ordering=order,
- )
- c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1)
- c.event("EndDialog", "Return", ordering=order + 2)
- c = seldlg.cancel("Cancel", "Features")
- c.event("SpawnDialog", "CancelDlg")
-
- c = seldlg.control(
- "Features",
- "SelectionTree",
- 15,
- 60,
- 300,
- 120,
- 3,
- "FEATURE",
- None,
- "PathEdit",
- None,
- )
- c.event("[FEATURE_SELECTED]", "1")
- ver = self.other_version
- install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver
- dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver
-
- c = seldlg.text(
- "Other", 15, 200, 300, 15, 3, "Provide an alternate Python location"
- )
- c.condition("Enable", install_other_cond)
- c.condition("Show", install_other_cond)
- c.condition("Disable", dont_install_other_cond)
- c.condition("Hide", dont_install_other_cond)
-
- c = seldlg.control(
- "PathEdit",
- "PathEdit",
- 15,
- 215,
- 300,
- 16,
- 1,
- "TARGETDIR" + ver,
- None,
- "Next",
- None,
- )
- c.condition("Enable", install_other_cond)
- c.condition("Show", install_other_cond)
- c.condition("Disable", dont_install_other_cond)
- c.condition("Hide", dont_install_other_cond)
-
- #####################################################################
- # Disk cost
- cost = PyDialog(
- db, "DiskCostDlg", x, y, w, h, modal, title, "OK", "OK", "OK", bitmap=False
- )
- cost.text(
- "Title", 15, 6, 200, 15, 0x30003, r"{\DlgFontBold8}Disk Space Requirements"
- )
- cost.text(
- "Description",
- 20,
- 20,
- 280,
- 20,
- 0x30003,
- "The disk space required for the installation of the selected features.",
- )
- cost.text(
- "Text",
- 20,
- 53,
- 330,
- 60,
- 3,
- "The highlighted volumes (if any) do not have enough disk space "
- "available for the currently selected features. You can either "
- "remove some files from the highlighted volumes, or choose to "
- "install less features onto local drive(s), or select different "
- "destination drive(s).",
- )
- cost.control(
- "VolumeList",
- "VolumeCostList",
- 20,
- 100,
- 330,
- 150,
- 393223,
- None,
- "{120}{70}{70}{70}{70}",
- None,
- None,
- )
- cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return")
-
- #####################################################################
- # WhichUsers Dialog. Only available on NT, and for privileged users.
- # This must be run before FindRelatedProducts, because that will
- # take into account whether the previous installation was per-user
- # or per-machine. We currently don't support going back to this
- # dialog after "Next" was selected; to support this, we would need to
- # find how to reset the ALLUSERS property, and how to re-run
- # FindRelatedProducts.
- # On Windows9x, the ALLUSERS property is ignored on the command line
- # and in the Property table, but installer fails according to the documentation
- # if a dialog attempts to set ALLUSERS.
- whichusers = PyDialog(
- db,
- "WhichUsersDlg",
- x,
- y,
- w,
- h,
- modal,
- title,
- "AdminInstall",
- "Next",
- "Cancel",
- )
- whichusers.title(
- "Select whether to install [ProductName] for all users of this computer."
- )
- # A radio group with two options: allusers, justme
- g = whichusers.radiogroup(
- "AdminInstall", 15, 60, 260, 50, 3, "WhichUsers", "", "Next"
- )
- g.add("ALL", 0, 5, 150, 20, "Install for all users")
- g.add("JUSTME", 0, 25, 150, 20, "Install just for me")
-
- whichusers.back("Back", None, active=0)
-
- c = whichusers.next("Next >", "Cancel")
- c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1)
- c.event("EndDialog", "Return", ordering=2)
-
- c = whichusers.cancel("Cancel", "AdminInstall")
- c.event("SpawnDialog", "CancelDlg")
-
- #####################################################################
- # Installation Progress dialog (modeless)
- progress = PyDialog(
- db,
- "ProgressDlg",
- x,
- y,
- w,
- h,
- modeless,
- title,
- "Cancel",
- "Cancel",
- "Cancel",
- bitmap=False,
- )
- progress.text(
- "Title",
- 20,
- 15,
- 200,
- 15,
- 0x30003,
- r"{\DlgFontBold8}[Progress1] [ProductName]",
- )
- progress.text(
- "Text",
- 35,
- 65,
- 300,
- 30,
- 3,
- "Please wait while the Installer [Progress2] [ProductName]. "
- "This may take several minutes.",
- )
- progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:")
-
- c = progress.text("ActionText", 70, 100, w - 70, 20, 3, "Pondering...")
- c.mapping("ActionText", "Text")
-
- # c=progress.text("ActionData", 35, 140, 300, 20, 3, None)
- # c.mapping("ActionData", "Text")
-
- c = progress.control(
- "ProgressBar",
- "ProgressBar",
- 35,
- 120,
- 300,
- 10,
- 65537,
- None,
- "Progress done",
- None,
- None,
- )
- c.mapping("SetProgress", "Progress")
-
- progress.back("< Back", "Next", active=False)
- progress.next("Next >", "Cancel", active=False)
- progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg")
-
- ###################################################################
- # Maintenance type: repair/uninstall
- maint = PyDialog(
- db, "MaintenanceTypeDlg", x, y, w, h, modal, title, "Next", "Next", "Cancel"
- )
- maint.title("Welcome to the [ProductName] Setup Wizard")
- maint.text(
- "BodyText",
- 15,
- 63,
- 330,
- 42,
- 3,
- "Select whether you want to repair or remove [ProductName].",
- )
- g = maint.radiogroup(
- "RepairRadioGroup",
- 15,
- 108,
- 330,
- 60,
- 3,
- "MaintenanceForm_Action",
- "",
- "Next",
- )
- # g.add("Change", 0, 0, 200, 17, "&Change [ProductName]")
- g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]")
- g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]")
-
- maint.back("< Back", None, active=False)
- c = maint.next("Finish", "Cancel")
- # Change installation: Change progress dialog to "Change", then ask
- # for feature selection
- # c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1)
- # c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2)
-
- # Reinstall: Change progress dialog to "Repair", then invoke reinstall
- # Also set list of reinstalled features to "ALL"
- c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5)
- c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6)
- c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7)
- c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8)
-
- # Uninstall: Change progress to "Remove", then invoke uninstall
- # Also set list of removed features to "ALL"
- c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11)
- c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12)
- c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13)
- c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14)
-
- # Close dialog when maintenance action scheduled
- c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20)
-
- maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg")
-
- def get_installer_filename(self, fullname):
- # Factored out to allow overriding in subclasses
- if self.target_version:
- base_name = "{}.{}-py{}.msi".format(
- fullname,
- self.plat_name,
- self.target_version,
- )
- else:
- base_name = "{}.{}.msi".format(fullname, self.plat_name)
- installer_name = os.path.join(self.dist_dir, base_name)
- return installer_name
+++ /dev/null
-"""distutils.command.bdist_wininst
-
-Implements the Distutils 'bdist_wininst' command: create a windows installer
-exe-program."""
-
-import os
-import sys
-import warnings
-from distutils.core import Command
-from distutils.util import get_platform
-from distutils.dir_util import remove_tree
-from distutils.errors import DistutilsOptionError, DistutilsPlatformError
-from distutils.sysconfig import get_python_version
-from distutils import log
-
-
-class bdist_wininst(Command):
-
- description = "create an executable installer for MS Windows"
-
- user_options = [
- ('bdist-dir=', None, "temporary directory for creating the distribution"),
- (
- 'plat-name=',
- 'p',
- "platform name to embed in generated filenames "
- "(default: %s)" % get_platform(),
- ),
- (
- 'keep-temp',
- 'k',
- "keep the pseudo-installation tree around after "
- + "creating the distribution archive",
- ),
- (
- 'target-version=',
- None,
- "require a specific python version" + " on the target system",
- ),
- ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"),
- (
- 'no-target-optimize',
- 'o',
- "do not compile .py to .pyo (optimized) " "on the target system",
- ),
- ('dist-dir=', 'd', "directory to put final built distributions in"),
- (
- 'bitmap=',
- 'b',
- "bitmap to use for the installer instead of python-powered logo",
- ),
- (
- 'title=',
- 't',
- "title to display on the installer background instead of default",
- ),
- ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
- (
- 'install-script=',
- None,
- "basename of installation script to be run after "
- "installation or before deinstallation",
- ),
- (
- 'pre-install-script=',
- None,
- "Fully qualified filename of a script to be run before "
- "any files are installed. This script need not be in the "
- "distribution",
- ),
- (
- 'user-access-control=',
- None,
- "specify Vista's UAC handling - 'none'/default=no "
- "handling, 'auto'=use UAC if target Python installed for "
- "all users, 'force'=always use UAC",
- ),
- ]
-
- boolean_options = [
- 'keep-temp',
- 'no-target-compile',
- 'no-target-optimize',
- 'skip-build',
- ]
-
- # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows
- _unsupported = sys.platform != "win32"
-
- def __init__(self, *args, **kw):
- super().__init__(*args, **kw)
- warnings.warn(
- "bdist_wininst command is deprecated since Python 3.8, "
- "use bdist_wheel (wheel packages) instead",
- DeprecationWarning,
- 2,
- )
-
- def initialize_options(self):
- self.bdist_dir = None
- self.plat_name = None
- self.keep_temp = 0
- self.no_target_compile = 0
- self.no_target_optimize = 0
- self.target_version = None
- self.dist_dir = None
- self.bitmap = None
- self.title = None
- self.skip_build = None
- self.install_script = None
- self.pre_install_script = None
- self.user_access_control = None
-
- def finalize_options(self):
- self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
-
- if self.bdist_dir is None:
- if self.skip_build and self.plat_name:
- # If build is skipped and plat_name is overridden, bdist will
- # not see the correct 'plat_name' - so set that up manually.
- bdist = self.distribution.get_command_obj('bdist')
- bdist.plat_name = self.plat_name
- # next the command will be initialized using that name
- bdist_base = self.get_finalized_command('bdist').bdist_base
- self.bdist_dir = os.path.join(bdist_base, 'wininst')
-
- if not self.target_version:
- self.target_version = ""
-
- if not self.skip_build and self.distribution.has_ext_modules():
- short_version = get_python_version()
- if self.target_version and self.target_version != short_version:
- raise DistutilsOptionError(
- "target version can only be %s, or the '--skip-build'"
- " option must be specified" % (short_version,)
- )
- self.target_version = short_version
-
- self.set_undefined_options(
- 'bdist',
- ('dist_dir', 'dist_dir'),
- ('plat_name', 'plat_name'),
- )
-
- if self.install_script:
- for script in self.distribution.scripts:
- if self.install_script == os.path.basename(script):
- break
- else:
- raise DistutilsOptionError(
- "install_script '%s' not found in scripts" % self.install_script
- )
-
- def run(self):
- if sys.platform != "win32" and (
- self.distribution.has_ext_modules() or self.distribution.has_c_libraries()
- ):
- raise DistutilsPlatformError(
- "distribution contains extensions and/or C libraries; "
- "must be compiled on a Windows 32 platform"
- )
-
- if not self.skip_build:
- self.run_command('build')
-
- install = self.reinitialize_command('install', reinit_subcommands=1)
- install.root = self.bdist_dir
- install.skip_build = self.skip_build
- install.warn_dir = 0
- install.plat_name = self.plat_name
-
- install_lib = self.reinitialize_command('install_lib')
- # we do not want to include pyc or pyo files
- install_lib.compile = 0
- install_lib.optimize = 0
-
- if self.distribution.has_ext_modules():
- # If we are building an installer for a Python version other
- # than the one we are currently running, then we need to ensure
- # our build_lib reflects the other Python version rather than ours.
- # Note that for target_version!=sys.version, we must have skipped the
- # build step, so there is no issue with enforcing the build of this
- # version.
- target_version = self.target_version
- if not target_version:
- assert self.skip_build, "Should have already checked this"
- target_version = '%d.%d' % sys.version_info[:2]
- plat_specifier = ".{}-{}".format(self.plat_name, target_version)
- build = self.get_finalized_command('build')
- build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier)
-
- # Use a custom scheme for the zip-file, because we have to decide
- # at installation time which scheme to use.
- for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
- value = key.upper()
- if key == 'headers':
- value = value + '/Include/$dist_name'
- setattr(install, 'install_' + key, value)
-
- log.info("installing to %s", self.bdist_dir)
- install.ensure_finalized()
-
- # avoid warning of 'install_lib' about installing
- # into a directory not in sys.path
- sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
-
- install.run()
-
- del sys.path[0]
-
- # And make an archive relative to the root of the
- # pseudo-installation tree.
- from tempfile import mktemp
-
- archive_basename = mktemp()
- fullname = self.distribution.get_fullname()
- arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir)
- # create an exe containing the zip-file
- self.create_exe(arcname, fullname, self.bitmap)
- if self.distribution.has_ext_modules():
- pyversion = get_python_version()
- else:
- pyversion = 'any'
- self.distribution.dist_files.append(
- ('bdist_wininst', pyversion, self.get_installer_filename(fullname))
- )
- # remove the zip-file again
- log.debug("removing temporary file '%s'", arcname)
- os.remove(arcname)
-
- if not self.keep_temp:
- remove_tree(self.bdist_dir, dry_run=self.dry_run)
-
- def get_inidata(self):
- # Return data describing the installation.
- lines = []
- metadata = self.distribution.metadata
-
- # Write the [metadata] section.
- lines.append("[metadata]")
-
- # 'info' will be displayed in the installer's dialog box,
- # describing the items to be installed.
- info = (metadata.long_description or '') + '\n'
-
- # Escape newline characters
- def escape(s):
- return s.replace("\n", "\\n")
-
- for name in [
- "author",
- "author_email",
- "description",
- "maintainer",
- "maintainer_email",
- "name",
- "url",
- "version",
- ]:
- data = getattr(metadata, name, "")
- if data:
- info = info + ("\n {}: {}".format(name.capitalize(), escape(data)))
- lines.append("{}={}".format(name, escape(data)))
-
- # The [setup] section contains entries controlling
- # the installer runtime.
- lines.append("\n[Setup]")
- if self.install_script:
- lines.append("install_script=%s" % self.install_script)
- lines.append("info=%s" % escape(info))
- lines.append("target_compile=%d" % (not self.no_target_compile))
- lines.append("target_optimize=%d" % (not self.no_target_optimize))
- if self.target_version:
- lines.append("target_version=%s" % self.target_version)
- if self.user_access_control:
- lines.append("user_access_control=%s" % self.user_access_control)
-
- title = self.title or self.distribution.get_fullname()
- lines.append("title=%s" % escape(title))
- import time
- import distutils
-
- build_info = "Built {} with distutils-{}".format(
- time.ctime(time.time()),
- distutils.__version__,
- )
- lines.append("build_info=%s" % build_info)
- return "\n".join(lines)
-
- def create_exe(self, arcname, fullname, bitmap=None):
- import struct
-
- self.mkpath(self.dist_dir)
-
- cfgdata = self.get_inidata()
-
- installer_name = self.get_installer_filename(fullname)
- self.announce("creating %s" % installer_name)
-
- if bitmap:
- with open(bitmap, "rb") as f:
- bitmapdata = f.read()
- bitmaplen = len(bitmapdata)
- else:
- bitmaplen = 0
-
- with open(installer_name, "wb") as file:
- file.write(self.get_exe_bytes())
- if bitmap:
- file.write(bitmapdata)
-
- # Convert cfgdata from unicode to ascii, mbcs encoded
- if isinstance(cfgdata, str):
- cfgdata = cfgdata.encode("mbcs")
-
- # Append the pre-install script
- cfgdata = cfgdata + b"\0"
- if self.pre_install_script:
- # We need to normalize newlines, so we open in text mode and
- # convert back to bytes. "latin-1" simply avoids any possible
- # failures.
- with open(self.pre_install_script, encoding="latin-1") as script:
- script_data = script.read().encode("latin-1")
- cfgdata = cfgdata + script_data + b"\n\0"
- else:
- # empty pre-install script
- cfgdata = cfgdata + b"\0"
- file.write(cfgdata)
-
- # The 'magic number' 0x1234567B is used to make sure that the
- # binary layout of 'cfgdata' is what the wininst.exe binary
- # expects. If the layout changes, increment that number, make
- # the corresponding changes to the wininst.exe sources, and
- # recompile them.
- header = struct.pack(
- "<iii",
- 0x1234567B, # tag
- len(cfgdata), # length
- bitmaplen, # number of bytes in bitmap
- )
- file.write(header)
- with open(arcname, "rb") as f:
- file.write(f.read())
-
- def get_installer_filename(self, fullname):
- # Factored out to allow overriding in subclasses
- if self.target_version:
- # if we create an installer for a specific python version,
- # it's better to include this in the name
- installer_name = os.path.join(
- self.dist_dir,
- "{}.{}-py{}.exe".format(fullname, self.plat_name, self.target_version),
- )
- else:
- installer_name = os.path.join(
- self.dist_dir, "{}.{}.exe".format(fullname, self.plat_name)
- )
- return installer_name
-
- def get_exe_bytes(self): # noqa: C901
- # If a target-version other than the current version has been
- # specified, then using the MSVC version from *this* build is no good.
- # Without actually finding and executing the target version and parsing
- # its sys.version, we just hard-code our knowledge of old versions.
- # NOTE: Possible alternative is to allow "--target-version" to
- # specify a Python executable rather than a simple version string.
- # We can then execute this program to obtain any info we need, such
- # as the real sys.version string for the build.
- cur_version = get_python_version()
-
- # If the target version is *later* than us, then we assume they
- # use what we use
- # string compares seem wrong, but are what sysconfig.py itself uses
- if self.target_version and self.target_version < cur_version:
- if self.target_version < "2.4":
- bv = '6.0'
- elif self.target_version == "2.4":
- bv = '7.1'
- elif self.target_version == "2.5":
- bv = '8.0'
- elif self.target_version <= "3.2":
- bv = '9.0'
- elif self.target_version <= "3.4":
- bv = '10.0'
- else:
- bv = '14.0'
- else:
- # for current version - use authoritative check.
- try:
- from msvcrt import CRT_ASSEMBLY_VERSION
- except ImportError:
- # cross-building, so assume the latest version
- bv = '14.0'
- else:
- # as far as we know, CRT is binary compatible based on
- # the first field, so assume 'x.0' until proven otherwise
- major = CRT_ASSEMBLY_VERSION.partition('.')[0]
- bv = major + '.0'
-
- # wininst-x.y.exe is in the same directory as this file
- directory = os.path.dirname(__file__)
- # we must use a wininst-x.y.exe built with the same C compiler
- # used for python. XXX What about mingw, borland, and so on?
-
- # if plat_name starts with "win" but is not "win32"
- # we want to strip "win" and leave the rest (e.g. -amd64)
- # for all other cases, we don't want any suffix
- if self.plat_name != 'win32' and self.plat_name[:3] == 'win':
- sfix = self.plat_name[3:]
- else:
- sfix = ''
-
- filename = os.path.join(directory, "wininst-{}{}.exe".format(bv, sfix))
- f = open(filename, "rb")
- try:
- return f.read()
- finally:
- f.close()
cygwin in no-cygwin mode).
"""
-# problems:
-#
-# * if you use a msvc compiled python version (1.5.2)
-# 1. you have to insert a __GNUC__ section in its config.h
-# 2. you have to generate an import library for its dll
-# - create a def-file for python??.dll
-# - create an import library using
-# dlltool --dllname python15.dll --def python15.def \
-# --output-lib libpython15.a
-#
-# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
-#
-# * We put export_symbols in a def-file, and don't use
-# --export-all-symbols because it doesn't worked reliable in some
-# tested configurations. And because other windows compilers also
-# need their symbols specified this no serious problem.
-#
-# tested configurations:
-#
-# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works
-# (after patching python's config.h and for C++ some other include files)
-# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
-# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works
-# (ld doesn't support -shared, so we use dllwrap)
-# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now
-# - its dllwrap doesn't work, there is a bug in binutils 2.10.90
-# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html
-# - using gcc -mdll instead dllwrap doesn't work without -static because
-# it tries to link against dlls instead their import libraries. (If
-# it finds the dll first.)
-# By specifying -static we force ld to link against the import libraries,
-# this is windows standard and there are normally not the necessary symbols
-# in the dlls.
-# *** only the version of June 2000 shows these problems
-# * cygwin gcc 3.2/ld 2.13.90 works
-# (ld supports -shared)
-# * mingw gcc 3.2/ld 2.13 works
-# (ld supports -shared)
-# * llvm-mingw with Clang 11 works
-# (lld supports -shared)
-
import os
import sys
import copy
+++ /dev/null
-"""distutils.msvc9compiler
-
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio 2008.
-
-The module is compatible with VS 2005 and VS 2008. You can find legacy support
-for older versions of VS in distutils.msvccompiler.
-"""
-
-# Written by Perry Stoll
-# hacked by Robin Becker and Thomas Heller to do a better job of
-# finding DevStudio (through the registry)
-# ported to VS2005 and VS 2008 by Christian Heimes
-
-import os
-import subprocess
-import sys
-import re
-
-from distutils.errors import (
- DistutilsExecError,
- DistutilsPlatformError,
- CompileError,
- LibError,
- LinkError,
-)
-from distutils.ccompiler import CCompiler, gen_lib_options
-from distutils import log
-from distutils.util import get_platform
-
-import winreg
-
-RegOpenKeyEx = winreg.OpenKeyEx
-RegEnumKey = winreg.EnumKey
-RegEnumValue = winreg.EnumValue
-RegError = winreg.error
-
-HKEYS = (
- winreg.HKEY_USERS,
- winreg.HKEY_CURRENT_USER,
- winreg.HKEY_LOCAL_MACHINE,
- winreg.HKEY_CLASSES_ROOT,
-)
-
-NATIVE_WIN64 = sys.platform == 'win32' and sys.maxsize > 2**32
-if NATIVE_WIN64:
- # Visual C++ is a 32-bit application, so we need to look in
- # the corresponding registry branch, if we're running a
- # 64-bit Python on Win64
- VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
- WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
- NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
-else:
- VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
- WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
- NET_BASE = r"Software\Microsoft\.NETFramework"
-
-# A map keyed by get_platform() return values to values accepted by
-# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
-# the param to cross-compile on x86 targeting amd64.)
-PLAT_TO_VCVARS = {
- 'win32': 'x86',
- 'win-amd64': 'amd64',
-}
-
-
-class Reg:
- """Helper class to read values from the registry"""
-
- def get_value(cls, path, key):
- for base in HKEYS:
- d = cls.read_values(base, path)
- if d and key in d:
- return d[key]
- raise KeyError(key)
-
- get_value = classmethod(get_value)
-
- def read_keys(cls, base, key):
- """Return list of registry keys."""
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- L = []
- i = 0
- while True:
- try:
- k = RegEnumKey(handle, i)
- except RegError:
- break
- L.append(k)
- i += 1
- return L
-
- read_keys = classmethod(read_keys)
-
- def read_values(cls, base, key):
- """Return dict of registry keys and values.
-
- All names are converted to lowercase.
- """
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- d = {}
- i = 0
- while True:
- try:
- name, value, type = RegEnumValue(handle, i)
- except RegError:
- break
- name = name.lower()
- d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
- i += 1
- return d
-
- read_values = classmethod(read_values)
-
- def convert_mbcs(s):
- dec = getattr(s, "decode", None)
- if dec is not None:
- try:
- s = dec("mbcs")
- except UnicodeError:
- pass
- return s
-
- convert_mbcs = staticmethod(convert_mbcs)
-
-
-class MacroExpander:
- def __init__(self, version):
- self.macros = {}
- self.vsbase = VS_BASE % version
- self.load_macros(version)
-
- def set_macro(self, macro, path, key):
- self.macros["$(%s)" % macro] = Reg.get_value(path, key)
-
- def load_macros(self, version):
- self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
- self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
- self.set_macro("FrameworkDir", NET_BASE, "installroot")
- try:
- if version >= 8.0:
- self.set_macro("FrameworkSDKDir", NET_BASE, "sdkinstallrootv2.0")
- else:
- raise KeyError("sdkinstallrootv2.0")
- except KeyError:
- raise DistutilsPlatformError(
- """Python was built with Visual Studio 2008;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2008 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py."""
- )
-
- if version >= 9.0:
- self.set_macro("FrameworkVersion", self.vsbase, "clr version")
- self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
- else:
- p = r"Software\Microsoft\NET Framework Setup\Product"
- for base in HKEYS:
- try:
- h = RegOpenKeyEx(base, p)
- except RegError:
- continue
- key = RegEnumKey(h, 0)
- d = Reg.get_value(base, r"{}\{}".format(p, key))
- self.macros["$(FrameworkVersion)"] = d["version"]
-
- def sub(self, s):
- for k, v in self.macros.items():
- s = s.replace(k, v)
- return s
-
-
-def get_build_version():
- """Return the version of MSVC that was used to build Python.
-
- For Python 2.3 and up, the version number is included in
- sys.version. For earlier versions, assume the compiler is MSVC 6.
- """
- prefix = "MSC v."
- i = sys.version.find(prefix)
- if i == -1:
- return 6
- i = i + len(prefix)
- s, rest = sys.version[i:].split(" ", 1)
- majorVersion = int(s[:-2]) - 6
- if majorVersion >= 13:
- # v13 was skipped and should be v14
- majorVersion += 1
- minorVersion = int(s[2:3]) / 10.0
- # I don't think paths are affected by minor version in version 6
- if majorVersion == 6:
- minorVersion = 0
- if majorVersion >= 6:
- return majorVersion + minorVersion
- # else we don't know what version of the compiler this is
- return None
-
-
-def normalize_and_reduce_paths(paths):
- """Return a list of normalized paths with duplicates removed.
-
- The current order of paths is maintained.
- """
- # Paths are normalized so things like: /a and /a/ aren't both preserved.
- reduced_paths = []
- for p in paths:
- np = os.path.normpath(p)
- # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
- if np not in reduced_paths:
- reduced_paths.append(np)
- return reduced_paths
-
-
-def removeDuplicates(variable):
- """Remove duplicate values of an environment variable."""
- oldList = variable.split(os.pathsep)
- newList = []
- for i in oldList:
- if i not in newList:
- newList.append(i)
- newVariable = os.pathsep.join(newList)
- return newVariable
-
-
-def find_vcvarsall(version):
- """Find the vcvarsall.bat file
-
- At first it tries to find the productdir of VS 2008 in the registry. If
- that fails it falls back to the VS90COMNTOOLS env var.
- """
- vsbase = VS_BASE % version
- try:
- productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, "productdir")
- except KeyError:
- log.debug("Unable to find productdir in registry")
- productdir = None
-
- if not productdir or not os.path.isdir(productdir):
- toolskey = "VS%0.f0COMNTOOLS" % version
- toolsdir = os.environ.get(toolskey, None)
-
- if toolsdir and os.path.isdir(toolsdir):
- productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
- productdir = os.path.abspath(productdir)
- if not os.path.isdir(productdir):
- log.debug("%s is not a valid directory" % productdir)
- return None
- else:
- log.debug("Env var %s is not set or invalid" % toolskey)
- if not productdir:
- log.debug("No productdir found")
- return None
- vcvarsall = os.path.join(productdir, "vcvarsall.bat")
- if os.path.isfile(vcvarsall):
- return vcvarsall
- log.debug("Unable to find vcvarsall.bat")
- return None
-
-
-def query_vcvarsall(version, arch="x86"):
- """Launch vcvarsall.bat and read the settings from its environment"""
- vcvarsall = find_vcvarsall(version)
- interesting = {"include", "lib", "libpath", "path"}
- result = {}
-
- if vcvarsall is None:
- raise DistutilsPlatformError("Unable to find vcvarsall.bat")
- log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
- popen = subprocess.Popen(
- '"{}" {} & set'.format(vcvarsall, arch),
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
- try:
- stdout, stderr = popen.communicate()
- if popen.wait() != 0:
- raise DistutilsPlatformError(stderr.decode("mbcs"))
-
- stdout = stdout.decode("mbcs")
- for line in stdout.split("\n"):
- line = Reg.convert_mbcs(line)
- if '=' not in line:
- continue
- line = line.strip()
- key, value = line.split('=', 1)
- key = key.lower()
- if key in interesting:
- if value.endswith(os.pathsep):
- value = value[:-1]
- result[key] = removeDuplicates(value)
-
- finally:
- popen.stdout.close()
- popen.stderr.close()
-
- if len(result) != len(interesting):
- raise ValueError(str(list(result.keys())))
-
- return result
-
-
-# More globals
-VERSION = get_build_version()
-# MACROS = MacroExpander(VERSION)
-
-
-class MSVCCompiler(CCompiler):
- """Concrete class that implements an interface to Microsoft Visual C++,
- as defined by the CCompiler abstract class."""
-
- compiler_type = 'msvc'
-
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
-
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
- _rc_extensions = ['.rc']
- _mc_extensions = ['.mc']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
- res_extension = '.res'
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=0, dry_run=0, force=0):
- super().__init__(verbose, dry_run, force)
- self.__version = VERSION
- self.__root = r"Software\Microsoft\VisualStudio"
- # self.__macros = MACROS
- self.__paths = []
- # target platform (.plat_name is consistent with 'bdist')
- self.plat_name = None
- self.__arch = None # deprecated name
- self.initialized = False
-
- def initialize(self, plat_name=None): # noqa: C901
- # multi-init means we would need to check platform same each time...
- assert not self.initialized, "don't init multiple times"
- if self.__version < 8.0:
- raise DistutilsPlatformError(
- "VC %0.1f is not supported by this module" % self.__version
- )
- if plat_name is None:
- plat_name = get_platform()
- # sanity check for platforms to prevent obscure errors later.
- ok_plats = 'win32', 'win-amd64'
- if plat_name not in ok_plats:
- raise DistutilsPlatformError(
- "--plat-name must be one of {}".format(ok_plats)
- )
-
- if (
- "DISTUTILS_USE_SDK" in os.environ
- and "MSSdk" in os.environ
- and self.find_exe("cl.exe")
- ):
- # Assume that the SDK set up everything alright; don't try to be
- # smarter
- self.cc = "cl.exe"
- self.linker = "link.exe"
- self.lib = "lib.exe"
- self.rc = "rc.exe"
- self.mc = "mc.exe"
- else:
- # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
- # to cross compile, you use 'x86_amd64'.
- # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
- # compile use 'x86' (ie, it runs the x86 compiler directly)
- if plat_name == get_platform() or plat_name == 'win32':
- # native build or cross-compile to win32
- plat_spec = PLAT_TO_VCVARS[plat_name]
- else:
- # cross compile from win32 -> some 64bit
- plat_spec = (
- PLAT_TO_VCVARS[get_platform()] + '_' + PLAT_TO_VCVARS[plat_name]
- )
-
- vc_env = query_vcvarsall(VERSION, plat_spec)
-
- self.__paths = vc_env['path'].split(os.pathsep)
- os.environ['lib'] = vc_env['lib']
- os.environ['include'] = vc_env['include']
-
- if len(self.__paths) == 0:
- raise DistutilsPlatformError(
- "Python was built with %s, "
- "and extensions need to be built with the same "
- "version of the compiler, but it isn't installed." % self.__product
- )
-
- self.cc = self.find_exe("cl.exe")
- self.linker = self.find_exe("link.exe")
- self.lib = self.find_exe("lib.exe")
- self.rc = self.find_exe("rc.exe") # resource compiler
- self.mc = self.find_exe("mc.exe") # message compiler
- # self.set_path_env_var('lib')
- # self.set_path_env_var('include')
-
- # extend the MSVC path with the current path
- try:
- for p in os.environ['path'].split(';'):
- self.__paths.append(p)
- except KeyError:
- pass
- self.__paths = normalize_and_reduce_paths(self.__paths)
- os.environ['path'] = ";".join(self.__paths)
-
- self.preprocess_options = None
- if self.__arch == "x86":
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/Z7',
- '/D_DEBUG',
- ]
- else:
- # Win64
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GS-',
- '/Z7',
- '/D_DEBUG',
- ]
-
- self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
- if self.__version >= 7:
- self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG']
- self.ldflags_static = ['/nologo']
-
- self.initialized = True
-
- # -- Worker methods ------------------------------------------------
-
- def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
- # Copied from ccompiler.py, extended to return .res as 'object'-file
- # for .rc input file
- if output_dir is None:
- output_dir = ''
- obj_names = []
- for src_name in source_filenames:
- (base, ext) = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
- if ext not in self.src_extensions:
- # Better to raise an exception instead of silently continuing
- # and later complain about sources and targets having
- # different lengths
- raise CompileError("Don't know how to compile %s" % src_name)
- if strip_dir:
- base = os.path.basename(base)
- if ext in self._rc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- elif ext in self._mc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- else:
- obj_names.append(os.path.join(output_dir, base + self.obj_extension))
- return obj_names
-
- def compile( # noqa: C901
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
-
- if not self.initialized:
- self.initialize()
- compile_info = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- macros, objects, extra_postargs, pp_opts, build = compile_info
-
- compile_opts = extra_preargs or []
- compile_opts.append('/c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- if debug:
- # pass the full pathname to MSVC in debug mode,
- # this allows the debugger to find the source file
- # without asking the user to browse for it
- src = os.path.abspath(src)
-
- if ext in self._c_extensions:
- input_opt = "/Tc" + src
- elif ext in self._cpp_extensions:
- input_opt = "/Tp" + src
- elif ext in self._rc_extensions:
- # compile .RC to .RES file
- input_opt = src
- output_opt = "/fo" + obj
- try:
- self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- elif ext in self._mc_extensions:
- # Compile .MC to .RC file to .RES file.
- # * '-h dir' specifies the directory for the
- # generated include file
- # * '-r dir' specifies the target directory of the
- # generated RC file and the binary message resource
- # it includes
- #
- # For now (since there are no options to change this),
- # we use the source-directory for the include file and
- # the build directory for the RC file and message
- # resources. This works at least for win32all.
- h_dir = os.path.dirname(src)
- rc_dir = os.path.dirname(obj)
- try:
- # first compile .MC to .RC and .H file
- self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src])
- base, _ = os.path.splitext(os.path.basename(src))
- rc_file = os.path.join(rc_dir, base + '.rc')
- # then compile .RC to .RES file
- self.spawn([self.rc] + ["/fo" + obj] + [rc_file])
-
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- else:
- # how to handle this file?
- raise CompileError(
- "Don't know how to compile {} to {}".format(src, obj)
- )
-
- output_opt = "/Fo" + obj
- try:
- self.spawn(
- [self.cc]
- + compile_opts
- + pp_opts
- + [input_opt, output_opt]
- + extra_postargs
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=0, target_lang=None
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- try:
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link( # noqa: C901
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- (libraries, library_dirs, runtime_library_dirs) = fixed_args
-
- if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- if target_desc == CCompiler.EXECUTABLE:
- if debug:
- ldflags = self.ldflags_shared_debug[1:]
- else:
- ldflags = self.ldflags_shared[1:]
- else:
- if debug:
- ldflags = self.ldflags_shared_debug
- else:
- ldflags = self.ldflags_shared
-
- export_opts = []
- for sym in export_symbols or []:
- export_opts.append("/EXPORT:" + sym)
-
- ld_args = (
- ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
- )
-
- # The MSVC linker generates .lib and .exp files, which cannot be
- # suppressed by any linker switches. The .lib files may even be
- # needed! Make sure they are generated in the temporary build
- # directory. Since they have different names for debug and release
- # builds, they can go into the same directory.
- build_temp = os.path.dirname(objects[0])
- if export_symbols is not None:
- (dll_name, dll_ext) = os.path.splitext(
- os.path.basename(output_filename)
- )
- implib_file = os.path.join(build_temp, self.library_filename(dll_name))
- ld_args.append('/IMPLIB:' + implib_file)
-
- self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- self.mkpath(os.path.dirname(output_filename))
- try:
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
-
- # embed the manifest
- # XXX - this is somewhat fragile - if mt.exe fails, distutils
- # will still consider the DLL up-to-date, but it will not have a
- # manifest. Maybe we should link to a temp file? OTOH, that
- # implies a build environment error that shouldn't go undetected.
- mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
- if mfinfo is not None:
- mffilename, mfid = mfinfo
- out_arg = '-outputresource:{};{}'.format(output_filename, mfid)
- try:
- self.spawn(['mt.exe', '-nologo', '-manifest', mffilename, out_arg])
- except DistutilsExecError as msg:
- raise LinkError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
- # If we need a manifest at all, an embedded manifest is recommended.
- # See MSDN article titled
- # "How to: Embed a Manifest Inside a C/C++ Application"
- # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
- # Ask the linker to generate the manifest in the temp dir, so
- # we can check it, and possibly embed it, later.
- temp_manifest = os.path.join(
- build_temp, os.path.basename(output_filename) + ".manifest"
- )
- ld_args.append('/MANIFESTFILE:' + temp_manifest)
-
- def manifest_get_embed_info(self, target_desc, ld_args):
- # If a manifest should be embedded, return a tuple of
- # (manifest_filename, resource_id). Returns None if no manifest
- # should be embedded. See http://bugs.python.org/issue7833 for why
- # we want to avoid any manifest for extension modules if we can)
- for arg in ld_args:
- if arg.startswith("/MANIFESTFILE:"):
- temp_manifest = arg.split(":", 1)[1]
- break
- else:
- # no /MANIFESTFILE so nothing to do.
- return None
- if target_desc == CCompiler.EXECUTABLE:
- # by default, executables always get the manifest with the
- # CRT referenced.
- mfid = 1
- else:
- # Extension modules try and avoid any manifest if possible.
- mfid = 2
- temp_manifest = self._remove_visual_c_ref(temp_manifest)
- if temp_manifest is None:
- return None
- return temp_manifest, mfid
-
- def _remove_visual_c_ref(self, manifest_file):
- try:
- # Remove references to the Visual C runtime, so they will
- # fall through to the Visual C dependency of Python.exe.
- # This way, when installed for a restricted user (e.g.
- # runtimes are not in WinSxS folder, but in Python's own
- # folder), the runtimes do not need to be in every folder
- # with .pyd's.
- # Returns either the filename of the modified manifest or
- # None if no manifest should be embedded.
- manifest_f = open(manifest_file)
- try:
- manifest_buf = manifest_f.read()
- finally:
- manifest_f.close()
- pattern = re.compile(
- r"""<assemblyIdentity.*?name=("|')Microsoft\."""
- r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
- re.DOTALL,
- )
- manifest_buf = re.sub(pattern, "", manifest_buf)
- pattern = r"<dependentAssembly>\s*</dependentAssembly>"
- manifest_buf = re.sub(pattern, "", manifest_buf)
- # Now see if any other assemblies are referenced - if not, we
- # don't want a manifest embedded.
- pattern = re.compile(
- r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
- r""".*?(?:/>|</assemblyIdentity>)""",
- re.DOTALL,
- )
- if re.search(pattern, manifest_buf) is None:
- return None
-
- manifest_f = open(manifest_file, 'w')
- try:
- manifest_f.write(manifest_buf)
- return manifest_file
- finally:
- manifest_f.close()
- except OSError:
- pass
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "/LIBPATH:" + dir
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path for MSVC++"
- )
-
- def library_option(self, lib):
- return self.library_filename(lib)
-
- def find_library_file(self, dirs, lib, debug=0):
- # Prefer a debugging library if found (and requested), but deal
- # with it if we don't have one.
- if debug:
- try_names = [lib + "_d", lib]
- else:
- try_names = [lib]
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.exists(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
- # Helper methods for using the MSVC registry settings
-
- def find_exe(self, exe):
- """Return path to an MSVC executable program.
-
- Tries to find the program in several places: first, one of the
- MSVC program search paths from the registry; next, the directories
- in the PATH environment variable. If any of those work, return an
- absolute path that is known to exist. If none of them work, just
- return the original program name, 'exe'.
- """
- for p in self.__paths:
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- # didn't find it; try existing path
- for p in os.environ['Path'].split(';'):
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- return exe
+++ /dev/null
-"""distutils.msvccompiler
-
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio.
-"""
-
-# Written by Perry Stoll
-# hacked by Robin Becker and Thomas Heller to do a better job of
-# finding DevStudio (through the registry)
-
-import sys
-import os
-from distutils.errors import (
- DistutilsExecError,
- DistutilsPlatformError,
- CompileError,
- LibError,
- LinkError,
-)
-from distutils.ccompiler import CCompiler, gen_lib_options
-from distutils import log
-
-_can_read_reg = False
-try:
- import winreg
-
- _can_read_reg = True
- hkey_mod = winreg
-
- RegOpenKeyEx = winreg.OpenKeyEx
- RegEnumKey = winreg.EnumKey
- RegEnumValue = winreg.EnumValue
- RegError = winreg.error
-
-except ImportError:
- try:
- import win32api
- import win32con
-
- _can_read_reg = True
- hkey_mod = win32con
-
- RegOpenKeyEx = win32api.RegOpenKeyEx
- RegEnumKey = win32api.RegEnumKey
- RegEnumValue = win32api.RegEnumValue
- RegError = win32api.error
- except ImportError:
- log.info(
- "Warning: Can't read registry to find the "
- "necessary compiler setting\n"
- "Make sure that Python modules winreg, "
- "win32api or win32con are installed."
- )
- pass
-
-if _can_read_reg:
- HKEYS = (
- hkey_mod.HKEY_USERS,
- hkey_mod.HKEY_CURRENT_USER,
- hkey_mod.HKEY_LOCAL_MACHINE,
- hkey_mod.HKEY_CLASSES_ROOT,
- )
-
-
-def read_keys(base, key):
- """Return list of registry keys."""
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- L = []
- i = 0
- while True:
- try:
- k = RegEnumKey(handle, i)
- except RegError:
- break
- L.append(k)
- i += 1
- return L
-
-
-def read_values(base, key):
- """Return dict of registry keys and values.
-
- All names are converted to lowercase.
- """
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- d = {}
- i = 0
- while True:
- try:
- name, value, type = RegEnumValue(handle, i)
- except RegError:
- break
- name = name.lower()
- d[convert_mbcs(name)] = convert_mbcs(value)
- i += 1
- return d
-
-
-def convert_mbcs(s):
- dec = getattr(s, "decode", None)
- if dec is not None:
- try:
- s = dec("mbcs")
- except UnicodeError:
- pass
- return s
-
-
-class MacroExpander:
- def __init__(self, version):
- self.macros = {}
- self.load_macros(version)
-
- def set_macro(self, macro, path, key):
- for base in HKEYS:
- d = read_values(base, path)
- if d:
- self.macros["$(%s)" % macro] = d[key]
- break
-
- def load_macros(self, version):
- vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
- self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
- self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
- net = r"Software\Microsoft\.NETFramework"
- self.set_macro("FrameworkDir", net, "installroot")
- try:
- if version > 7.0:
- self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
- else:
- self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
- except KeyError:
- raise DistutilsPlatformError(
- """Python was built with Visual Studio 2003;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2003 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py."""
- )
-
- p = r"Software\Microsoft\NET Framework Setup\Product"
- for base in HKEYS:
- try:
- h = RegOpenKeyEx(base, p)
- except RegError:
- continue
- key = RegEnumKey(h, 0)
- d = read_values(base, r"{}\{}".format(p, key))
- self.macros["$(FrameworkVersion)"] = d["version"]
-
- def sub(self, s):
- for k, v in self.macros.items():
- s = s.replace(k, v)
- return s
-
-
-def get_build_version():
- """Return the version of MSVC that was used to build Python.
-
- For Python 2.3 and up, the version number is included in
- sys.version. For earlier versions, assume the compiler is MSVC 6.
- """
- prefix = "MSC v."
- i = sys.version.find(prefix)
- if i == -1:
- return 6
- i = i + len(prefix)
- s, rest = sys.version[i:].split(" ", 1)
- majorVersion = int(s[:-2]) - 6
- if majorVersion >= 13:
- # v13 was skipped and should be v14
- majorVersion += 1
- minorVersion = int(s[2:3]) / 10.0
- # I don't think paths are affected by minor version in version 6
- if majorVersion == 6:
- minorVersion = 0
- if majorVersion >= 6:
- return majorVersion + minorVersion
- # else we don't know what version of the compiler this is
- return None
-
-
-def get_build_architecture():
- """Return the processor architecture.
-
- Possible results are "Intel" or "AMD64".
- """
-
- prefix = " bit ("
- i = sys.version.find(prefix)
- if i == -1:
- return "Intel"
- j = sys.version.find(")", i)
- return sys.version[i + len(prefix) : j]
-
-
-def normalize_and_reduce_paths(paths):
- """Return a list of normalized paths with duplicates removed.
-
- The current order of paths is maintained.
- """
- # Paths are normalized so things like: /a and /a/ aren't both preserved.
- reduced_paths = []
- for p in paths:
- np = os.path.normpath(p)
- # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
- if np not in reduced_paths:
- reduced_paths.append(np)
- return reduced_paths
-
-
-class MSVCCompiler(CCompiler):
- """Concrete class that implements an interface to Microsoft Visual C++,
- as defined by the CCompiler abstract class."""
-
- compiler_type = 'msvc'
-
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
-
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
- _rc_extensions = ['.rc']
- _mc_extensions = ['.mc']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
- res_extension = '.res'
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=0, dry_run=0, force=0):
- super().__init__(verbose, dry_run, force)
- self.__version = get_build_version()
- self.__arch = get_build_architecture()
- if self.__arch == "Intel":
- # x86
- if self.__version >= 7:
- self.__root = r"Software\Microsoft\VisualStudio"
- self.__macros = MacroExpander(self.__version)
- else:
- self.__root = r"Software\Microsoft\Devstudio"
- self.__product = "Visual Studio version %s" % self.__version
- else:
- # Win64. Assume this was built with the platform SDK
- self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
-
- self.initialized = False
-
- def initialize(self):
- self.__paths = []
- if (
- "DISTUTILS_USE_SDK" in os.environ
- and "MSSdk" in os.environ
- and self.find_exe("cl.exe")
- ):
- # Assume that the SDK set up everything alright; don't try to be
- # smarter
- self.cc = "cl.exe"
- self.linker = "link.exe"
- self.lib = "lib.exe"
- self.rc = "rc.exe"
- self.mc = "mc.exe"
- else:
- self.__paths = self.get_msvc_paths("path")
-
- if len(self.__paths) == 0:
- raise DistutilsPlatformError(
- "Python was built with %s, "
- "and extensions need to be built with the same "
- "version of the compiler, but it isn't installed." % self.__product
- )
-
- self.cc = self.find_exe("cl.exe")
- self.linker = self.find_exe("link.exe")
- self.lib = self.find_exe("lib.exe")
- self.rc = self.find_exe("rc.exe") # resource compiler
- self.mc = self.find_exe("mc.exe") # message compiler
- self.set_path_env_var('lib')
- self.set_path_env_var('include')
-
- # extend the MSVC path with the current path
- try:
- for p in os.environ['path'].split(';'):
- self.__paths.append(p)
- except KeyError:
- pass
- self.__paths = normalize_and_reduce_paths(self.__paths)
- os.environ['path'] = ";".join(self.__paths)
-
- self.preprocess_options = None
- if self.__arch == "Intel":
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GX',
- '/Z7',
- '/D_DEBUG',
- ]
- else:
- # Win64
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GS-',
- '/Z7',
- '/D_DEBUG',
- ]
-
- self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
- if self.__version >= 7:
- self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG']
- else:
- self.ldflags_shared_debug = [
- '/DLL',
- '/nologo',
- '/INCREMENTAL:no',
- '/pdb:None',
- '/DEBUG',
- ]
- self.ldflags_static = ['/nologo']
-
- self.initialized = True
-
- # -- Worker methods ------------------------------------------------
-
- def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
- # Copied from ccompiler.py, extended to return .res as 'object'-file
- # for .rc input file
- if output_dir is None:
- output_dir = ''
- obj_names = []
- for src_name in source_filenames:
- (base, ext) = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
- if ext not in self.src_extensions:
- # Better to raise an exception instead of silently continuing
- # and later complain about sources and targets having
- # different lengths
- raise CompileError("Don't know how to compile %s" % src_name)
- if strip_dir:
- base = os.path.basename(base)
- if ext in self._rc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- elif ext in self._mc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- else:
- obj_names.append(os.path.join(output_dir, base + self.obj_extension))
- return obj_names
-
- def compile( # noqa: C901
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
-
- if not self.initialized:
- self.initialize()
- compile_info = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- macros, objects, extra_postargs, pp_opts, build = compile_info
-
- compile_opts = extra_preargs or []
- compile_opts.append('/c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- if debug:
- # pass the full pathname to MSVC in debug mode,
- # this allows the debugger to find the source file
- # without asking the user to browse for it
- src = os.path.abspath(src)
-
- if ext in self._c_extensions:
- input_opt = "/Tc" + src
- elif ext in self._cpp_extensions:
- input_opt = "/Tp" + src
- elif ext in self._rc_extensions:
- # compile .RC to .RES file
- input_opt = src
- output_opt = "/fo" + obj
- try:
- self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- elif ext in self._mc_extensions:
- # Compile .MC to .RC file to .RES file.
- # * '-h dir' specifies the directory for the
- # generated include file
- # * '-r dir' specifies the target directory of the
- # generated RC file and the binary message resource
- # it includes
- #
- # For now (since there are no options to change this),
- # we use the source-directory for the include file and
- # the build directory for the RC file and message
- # resources. This works at least for win32all.
- h_dir = os.path.dirname(src)
- rc_dir = os.path.dirname(obj)
- try:
- # first compile .MC to .RC and .H file
- self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src])
- base, _ = os.path.splitext(os.path.basename(src))
- rc_file = os.path.join(rc_dir, base + '.rc')
- # then compile .RC to .RES file
- self.spawn([self.rc] + ["/fo" + obj] + [rc_file])
-
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- else:
- # how to handle this file?
- raise CompileError(
- "Don't know how to compile {} to {}".format(src, obj)
- )
-
- output_opt = "/Fo" + obj
- try:
- self.spawn(
- [self.cc]
- + compile_opts
- + pp_opts
- + [input_opt, output_opt]
- + extra_postargs
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=0, target_lang=None
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- try:
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link( # noqa: C901
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- (libraries, library_dirs, runtime_library_dirs) = fixed_args
-
- if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- if target_desc == CCompiler.EXECUTABLE:
- if debug:
- ldflags = self.ldflags_shared_debug[1:]
- else:
- ldflags = self.ldflags_shared[1:]
- else:
- if debug:
- ldflags = self.ldflags_shared_debug
- else:
- ldflags = self.ldflags_shared
-
- export_opts = []
- for sym in export_symbols or []:
- export_opts.append("/EXPORT:" + sym)
-
- ld_args = (
- ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
- )
-
- # The MSVC linker generates .lib and .exp files, which cannot be
- # suppressed by any linker switches. The .lib files may even be
- # needed! Make sure they are generated in the temporary build
- # directory. Since they have different names for debug and release
- # builds, they can go into the same directory.
- if export_symbols is not None:
- (dll_name, dll_ext) = os.path.splitext(
- os.path.basename(output_filename)
- )
- implib_file = os.path.join(
- os.path.dirname(objects[0]), self.library_filename(dll_name)
- )
- ld_args.append('/IMPLIB:' + implib_file)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- self.mkpath(os.path.dirname(output_filename))
- try:
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
-
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "/LIBPATH:" + dir
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path for MSVC++"
- )
-
- def library_option(self, lib):
- return self.library_filename(lib)
-
- def find_library_file(self, dirs, lib, debug=0):
- # Prefer a debugging library if found (and requested), but deal
- # with it if we don't have one.
- if debug:
- try_names = [lib + "_d", lib]
- else:
- try_names = [lib]
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.exists(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
- # Helper methods for using the MSVC registry settings
-
- def find_exe(self, exe):
- """Return path to an MSVC executable program.
-
- Tries to find the program in several places: first, one of the
- MSVC program search paths from the registry; next, the directories
- in the PATH environment variable. If any of those work, return an
- absolute path that is known to exist. If none of them work, just
- return the original program name, 'exe'.
- """
- for p in self.__paths:
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- # didn't find it; try existing path
- for p in os.environ['Path'].split(';'):
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- return exe
-
- def get_msvc_paths(self, path, platform='x86'):
- """Get a list of devstudio directories (include, lib or path).
-
- Return a list of strings. The list will be empty if unable to
- access the registry or appropriate registry keys not found.
- """
- if not _can_read_reg:
- return []
-
- path = path + " dirs"
- if self.__version >= 7:
- key = r"{}\{:0.1f}\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories".format(
- self.__root,
- self.__version,
- )
- else:
- key = (
- r"%s\6.0\Build System\Components\Platforms"
- r"\Win32 (%s)\Directories" % (self.__root, platform)
- )
-
- for base in HKEYS:
- d = read_values(base, key)
- if d:
- if self.__version >= 7:
- return self.__macros.sub(d[path]).split(";")
- else:
- return d[path].split(";")
- # MSVC 6 seems to create the registry entries we need only when
- # the GUI is run.
- if self.__version == 6:
- for base in HKEYS:
- if read_values(base, r"%s\6.0" % self.__root) is not None:
- self.warn(
- "It seems you have Visual Studio 6 installed, "
- "but the expected registry settings are not present.\n"
- "You must at least run the Visual Studio GUI once "
- "so that these entries are created."
- )
- break
- return []
-
- def set_path_env_var(self, name):
- """Set environment variable 'name' to an MSVC path type value.
-
- This is equivalent to a SET command prior to execution of spawned
- commands.
- """
-
- if name == "lib":
- p = self.get_msvc_paths("library")
- else:
- p = self.get_msvc_paths(name)
- if p:
- os.environ[name] = ';'.join(p)
-
-
-if get_build_version() >= 8.0:
- log.debug("Importing new compiler from distutils.msvc9compiler")
- OldMSVCCompiler = MSVCCompiler
- from distutils.msvc9compiler import MSVCCompiler
-
- # get_build_architecture not really relevant now we support cross-compile
- from distutils.msvc9compiler import MacroExpander # noqa: F811
"""Tests for distutils.command.bdist."""
-import os
-import warnings
-
from distutils.command.bdist import bdist
from distutils.tests import support
# we can set the format
dist = self.create_dist()[1]
cmd = bdist(dist)
- cmd.formats = ['msi']
+ cmd.formats = ['gztar']
cmd.ensure_finalized()
- assert cmd.formats == ['msi']
+ assert cmd.formats == ['gztar']
# what formats does bdist offer?
formats = [
'bztar',
'gztar',
- 'msi',
'rpm',
'tar',
- 'wininst',
'xztar',
'zip',
'ztar',
names = [
'bdist_dumb',
- 'bdist_wininst',
] # bdist_rpm does not support --skip-build
- if os.name == 'nt':
- names.append('bdist_msi')
for name in names:
- with warnings.catch_warnings():
- warnings.filterwarnings(
- 'ignore', 'bdist_wininst command is deprecated', DeprecationWarning
- )
- subcmd = cmd.get_finalized_command(name)
+ subcmd = cmd.get_finalized_command(name)
if getattr(subcmd, '_unsupported', False):
# command is not supported on this build
continue
+++ /dev/null
-"""Tests for distutils.command.bdist_msi."""
-import pytest
-
-from distutils.tests import support
-
-from .py38compat import check_warnings
-
-
-pytest.importorskip('msilib')
-
-
-class TestBDistMSI(support.TempdirManager, support.LoggingSilencer):
- def test_minimal(self):
- # minimal test XXX need more tests
- from distutils.command.bdist_msi import bdist_msi
-
- project_dir, dist = self.create_dist()
- with check_warnings(("", DeprecationWarning)):
- cmd = bdist_msi(dist)
- cmd.ensure_finalized()
+++ /dev/null
-"""Tests for distutils.command.bdist_wininst."""
-import pytest
-
-from .py38compat import check_warnings
-
-from distutils.command.bdist_wininst import bdist_wininst
-from distutils.tests import support
-
-
-@pytest.mark.skipif("platform.machine() == 'ARM64'")
-@pytest.mark.skipif("bdist_wininst._unsupported")
-class TestBuildWinInst(support.TempdirManager, support.LoggingSilencer):
- def test_get_exe_bytes(self):
-
- # issue5731: command was broken on non-windows platforms
- # this test makes sure it works now for every platform
- # let's create a command
- pkg_pth, dist = self.create_dist()
- with check_warnings(("", DeprecationWarning)):
- cmd = bdist_wininst(dist)
- cmd.ensure_finalized()
-
- # let's run the code that finds the right wininst*.exe file
- # and make sure it finds it and returns its content
- # no matter what platform we have
- exe_file = cmd.get_exe_bytes()
- assert len(exe_file) > 10
+++ /dev/null
-"""Tests for distutils.msvc9compiler."""
-import sys
-import os
-
-from distutils.errors import DistutilsPlatformError
-from distutils.tests import support
-import pytest
-
-# A manifest with the only assembly reference being the msvcrt assembly, so
-# should have the assembly completely stripped. Note that although the
-# assembly has a <security> reference the assembly is removed - that is
-# currently a "feature", not a bug :)
-_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX">
- </assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>
-"""
-
-# A manifest with references to assemblies other than msvcrt. When processed,
-# this assembly should be returned with just the msvcrt part removed.
-_MANIFEST_WITH_MULTIPLE_REFERENCES = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX">
- </assemblyIdentity>
- </dependentAssembly>
- </dependency>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX"></assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>
-"""
-
-_CLEANED_MANIFEST = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
-
- </dependency>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX"></assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>"""
-
-if sys.platform == "win32":
- from distutils.msvccompiler import get_build_version
-
- if get_build_version() >= 8.0:
- SKIP_MESSAGE = None
- else:
- SKIP_MESSAGE = "These tests are only for MSVC8.0 or above"
-else:
- SKIP_MESSAGE = "These tests are only for win32"
-
-
-@pytest.mark.skipif('SKIP_MESSAGE', reason=SKIP_MESSAGE)
-class Testmsvc9compiler(support.TempdirManager):
- def test_no_compiler(self):
- # makes sure query_vcvarsall raises
- # a DistutilsPlatformError if the compiler
- # is not found
- from distutils.msvc9compiler import query_vcvarsall
-
- def _find_vcvarsall(version):
- return None
-
- from distutils import msvc9compiler
-
- old_find_vcvarsall = msvc9compiler.find_vcvarsall
- msvc9compiler.find_vcvarsall = _find_vcvarsall
- try:
- with pytest.raises(DistutilsPlatformError):
- query_vcvarsall('wont find this version')
- finally:
- msvc9compiler.find_vcvarsall = old_find_vcvarsall
-
- def test_reg_class(self):
- from distutils.msvc9compiler import Reg
-
- with pytest.raises(KeyError):
- Reg.get_value('xxx', 'xxx')
-
- # looking for values that should exist on all
- # windows registry versions.
- path = r'Control Panel\Desktop'
- v = Reg.get_value(path, 'dragfullwindows')
- assert v in ('0', '1', '2')
-
- import winreg
-
- HKCU = winreg.HKEY_CURRENT_USER
- keys = Reg.read_keys(HKCU, 'xxxx')
- assert keys is None
-
- keys = Reg.read_keys(HKCU, r'Control Panel')
- assert 'Desktop' in keys
-
- def test_remove_visual_c_ref(self):
- from distutils.msvc9compiler import MSVCCompiler
-
- tempdir = self.mkdtemp()
- manifest = os.path.join(tempdir, 'manifest')
- f = open(manifest, 'w')
- try:
- f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES)
- finally:
- f.close()
-
- compiler = MSVCCompiler()
- compiler._remove_visual_c_ref(manifest)
-
- # see what we got
- f = open(manifest)
- try:
- # removing trailing spaces
- content = '\n'.join([line.rstrip() for line in f.readlines()])
- finally:
- f.close()
-
- # makes sure the manifest was properly cleaned
- assert content == _CLEANED_MANIFEST
-
- def test_remove_entire_manifest(self):
- from distutils.msvc9compiler import MSVCCompiler
-
- tempdir = self.mkdtemp()
- manifest = os.path.join(tempdir, 'manifest')
- f = open(manifest, 'w')
- try:
- f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE)
- finally:
- f.close()
-
- compiler = MSVCCompiler()
- got = compiler._remove_visual_c_ref(manifest)
- assert got is None
-@echo off\r
-\r
-REM Use old Windows SDK 6.1 so created .exe will be compatible with\r
-REM old Windows versions.\r
-REM Windows SDK 6.1 may be downloaded at:\r
-REM http://www.microsoft.com/en-us/download/details.aspx?id=11310\r
-set PATH_OLD=%PATH%\r
-\r
-REM The SDK creates a false install of Visual Studio at one of these locations\r
-set PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC\bin;%PATH%\r
-set PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin;%PATH%\r
-\r
-REM set up the environment to compile to x86\r
-call VCVARS32\r
-if "%ERRORLEVEL%"=="0" (\r
- cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:CONSOLE /out:setuptools/cli-32.exe\r
- cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:WINDOWS /out:setuptools/gui-32.exe\r
-) else (\r
- echo Windows SDK 6.1 not found to build Windows 32-bit version\r
-)\r
-\r
-REM buildout (and possibly other implementations) currently depend on\r
-REM the 32-bit launcher scripts without the -32 in the filename, so copy them\r
-REM there for now.\r
-copy setuptools/cli-32.exe setuptools/cli.exe\r
-copy setuptools/gui-32.exe setuptools/gui.exe\r
-\r
-REM now for 64-bit\r
-REM Use the x86_amd64 profile, which is the 32-bit cross compiler for amd64\r
-call VCVARSx86_amd64\r
-if "%ERRORLEVEL%"=="0" (\r
- cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:CONSOLE /out:setuptools/cli-64.exe\r
- cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:WINDOWS /out:setuptools/gui-64.exe\r
-) else (\r
- echo Windows SDK 6.1 not found to build Windows 64-bit version\r
-)\r
-\r
-set PATH=%PATH_OLD%\r
-\r
+@echo off
+
+REM Use old Windows SDK 6.1 so created .exe will be compatible with
+REM old Windows versions.
+REM Windows SDK 6.1 may be downloaded at:
+REM http://www.microsoft.com/en-us/download/details.aspx?id=11310
+set PATH_OLD=%PATH%
+
+REM The SDK creates a false install of Visual Studio at one of these locations
+set PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC\bin;%PATH%
+set PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin;%PATH%
+
+REM set up the environment to compile to x86
+call VCVARS32
+if "%ERRORLEVEL%"=="0" (
+ cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:CONSOLE /out:setuptools/cli-32.exe
+ cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:WINDOWS /out:setuptools/gui-32.exe
+) else (
+ echo Windows SDK 6.1 not found to build Windows 32-bit version
+)
+
+REM buildout (and possibly other implementations) currently depend on
+REM the 32-bit launcher scripts without the -32 in the filename, so copy them
+REM there for now.
+copy setuptools/cli-32.exe setuptools/cli.exe
+copy setuptools/gui-32.exe setuptools/gui.exe
+
+REM now for 64-bit
+REM Use the x86_amd64 profile, which is the 32-bit cross compiler for amd64
+call VCVARSx86_amd64
+if "%ERRORLEVEL%"=="0" (
+ cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:CONSOLE /out:setuptools/cli-64.exe
+ cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:WINDOWS /out:setuptools/gui-64.exe
+) else (
+ echo Windows SDK 6.1 not found to build Windows 64-bit version
+)
+
+set PATH=%PATH_OLD%
+