up
This commit is contained in:
@@ -1,36 +1,26 @@
|
||||
"""Extensions to the 'distutils' for large or complex distributions"""
|
||||
# mypy: disable_error_code=override
|
||||
# Command.reinitialize_command has an extra **kw param that distutils doesn't have
|
||||
# Can't disable on the exact line because distutils doesn't exists on Python 3.12
|
||||
# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any,
|
||||
# and a [unused-ignore] to be raised on 3.12+
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, TypeVar, overload
|
||||
|
||||
sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip
|
||||
# workaround for #4476
|
||||
sys.modules.pop('backports', None)
|
||||
import warnings
|
||||
|
||||
import _distutils_hack.override # noqa: F401
|
||||
|
||||
from . import logging, monkey
|
||||
from .depends import Require
|
||||
from .discovery import PackageFinder, PEP420PackageFinder
|
||||
from .dist import Distribution
|
||||
from .extension import Extension
|
||||
from .version import __version__ as __version__
|
||||
from .warnings import SetuptoolsDeprecationWarning
|
||||
|
||||
import distutils.core
|
||||
from distutils.errors import DistutilsOptionError
|
||||
from distutils.util import convert_path as _convert_path
|
||||
|
||||
from ._deprecation_warning import SetuptoolsDeprecationWarning
|
||||
|
||||
import setuptools.version
|
||||
from setuptools.extension import Extension
|
||||
from setuptools.dist import Distribution
|
||||
from setuptools.depends import Require
|
||||
from setuptools.discovery import PackageFinder, PEP420PackageFinder
|
||||
from . import monkey
|
||||
from . import logging
|
||||
|
||||
|
||||
__all__ = [
|
||||
'setup',
|
||||
@@ -43,10 +33,11 @@ __all__ = [
|
||||
'find_namespace_packages',
|
||||
]
|
||||
|
||||
_CommandT = TypeVar("_CommandT", bound="_Command")
|
||||
__version__ = setuptools.version.__version__
|
||||
|
||||
bootstrap_install_from = None
|
||||
|
||||
|
||||
find_packages = PackageFinder.find
|
||||
find_namespace_packages = PEP420PackageFinder.find
|
||||
|
||||
@@ -60,7 +51,7 @@ def _install_setup_requires(attrs):
|
||||
fetch_build_eggs interface.
|
||||
"""
|
||||
|
||||
def __init__(self, attrs: Mapping[str, object]) -> None:
|
||||
def __init__(self, attrs):
|
||||
_incl = 'dependency_links', 'setup_requires'
|
||||
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
|
||||
super().__init__(filtered)
|
||||
@@ -70,10 +61,10 @@ def _install_setup_requires(attrs):
|
||||
def _get_project_config_files(self, filenames=None):
|
||||
"""Ignore ``pyproject.toml``, they are not related to setup_requires"""
|
||||
try:
|
||||
cfg, _toml = super()._split_standard_project_metadata(filenames)
|
||||
cfg, toml = super()._split_standard_project_metadata(filenames)
|
||||
return cfg, ()
|
||||
except Exception:
|
||||
return filenames, ()
|
||||
return cfg, ()
|
||||
|
||||
def finalize_options(self):
|
||||
"""
|
||||
@@ -86,44 +77,20 @@ def _install_setup_requires(attrs):
|
||||
# Honor setup.cfg's options.
|
||||
dist.parse_config_files(ignore_option_errors=True)
|
||||
if dist.setup_requires:
|
||||
_fetch_build_eggs(dist)
|
||||
|
||||
|
||||
def _fetch_build_eggs(dist: Distribution):
|
||||
try:
|
||||
dist.fetch_build_eggs(dist.setup_requires)
|
||||
except Exception as ex:
|
||||
msg = """
|
||||
It is possible a package already installed in your system
|
||||
contains an version that is invalid according to PEP 440.
|
||||
You can try `pip install --use-pep517` as a workaround for this problem,
|
||||
or rely on a new virtual environment.
|
||||
|
||||
If the problem refers to a package that is not installed yet,
|
||||
please contact that package's maintainers or distributors.
|
||||
"""
|
||||
if "InvalidVersion" in ex.__class__.__name__:
|
||||
if hasattr(ex, "add_note"):
|
||||
ex.add_note(msg) # PEP 678
|
||||
else:
|
||||
dist.announce(f"\n{msg}\n")
|
||||
raise
|
||||
|
||||
|
||||
def setup(**attrs):
|
||||
logging.configure()
|
||||
# Make sure we have any requirements needed to interpret 'attrs'.
|
||||
logging.configure()
|
||||
_install_setup_requires(attrs)
|
||||
return distutils.core.setup(**attrs)
|
||||
|
||||
|
||||
setup.__doc__ = distutils.core.setup.__doc__
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
|
||||
from distutils.core import Command as _Command
|
||||
else:
|
||||
_Command = monkey.get_unpatched(distutils.core.Command)
|
||||
|
||||
_Command = monkey.get_unpatched(distutils.core.Command)
|
||||
|
||||
|
||||
class Command(_Command):
|
||||
@@ -139,25 +106,42 @@ class Command(_Command):
|
||||
When creating a new command from scratch, custom defined classes **SHOULD** inherit
|
||||
from ``setuptools.Command`` and implement a few mandatory methods.
|
||||
Between these mandatory methods, are listed:
|
||||
:meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`.
|
||||
|
||||
.. method:: initialize_options(self)
|
||||
|
||||
Set or (reset) all options/attributes/caches used by the command
|
||||
to their default values. Note that these values may be overwritten during
|
||||
the build.
|
||||
|
||||
.. method:: finalize_options(self)
|
||||
|
||||
Set final values for all options/attributes used by the command.
|
||||
Most of the time, each option/attribute/cache should only be set if it does not
|
||||
have any value yet (e.g. ``if self.attr is None: self.attr = val``).
|
||||
|
||||
.. method:: run(self)
|
||||
|
||||
Execute the actions intended by the command.
|
||||
(Side effects **SHOULD** only take place when ``run`` is executed,
|
||||
for example, creating new files or writing to the terminal output).
|
||||
|
||||
A useful analogy for command classes is to think of them as subroutines with local
|
||||
variables called "options". The options are "declared" in :meth:`initialize_options`
|
||||
and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`,
|
||||
variables called "options". The options are "declared" in ``initialize_options()``
|
||||
and "defined" (given their final values, aka "finalized") in ``finalize_options()``,
|
||||
both of which must be defined by every command class. The "body" of the subroutine,
|
||||
(where it does all the work) is the :meth:`run` method.
|
||||
Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set
|
||||
(where it does all the work) is the ``run()`` method.
|
||||
Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set
|
||||
the values for options/attributes based on user's input (or circumstance),
|
||||
which means that the implementation should be careful to not overwrite values in
|
||||
:meth:`finalize_options` unless necessary.
|
||||
``finalize_options`` unless necessary.
|
||||
|
||||
Please note that other commands (or other parts of setuptools) may also overwrite
|
||||
the values of the command's options/attributes multiple times during the build
|
||||
process.
|
||||
Therefore it is important to consistently implement :meth:`initialize_options` and
|
||||
:meth:`finalize_options`. For example, all derived attributes (or attributes that
|
||||
Therefore it is important to consistently implement ``initialize_options()`` and
|
||||
``finalize_options()``. For example, all derived attributes (or attributes that
|
||||
depend on the value of other attributes) **SHOULD** be recomputed in
|
||||
:meth:`finalize_options`.
|
||||
``finalize_options``.
|
||||
|
||||
When overwriting existing commands, custom defined classes **MUST** abide by the
|
||||
same APIs implemented by the original class. They also **SHOULD** inherit from the
|
||||
@@ -165,9 +149,8 @@ class Command(_Command):
|
||||
"""
|
||||
|
||||
command_consumes_arguments = False
|
||||
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
|
||||
|
||||
def __init__(self, dist: Distribution, **kw) -> None:
|
||||
def __init__(self, dist, **kw):
|
||||
"""
|
||||
Construct the command for dist, updating
|
||||
vars(self) with any keyword parameters.
|
||||
@@ -181,10 +164,12 @@ class Command(_Command):
|
||||
setattr(self, option, default)
|
||||
return default
|
||||
elif not isinstance(val, str):
|
||||
raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
|
||||
raise DistutilsOptionError(
|
||||
"'%s' must be a %s (got `%s`)" % (option, what, val)
|
||||
)
|
||||
return val
|
||||
|
||||
def ensure_string_list(self, option: str) -> None:
|
||||
def ensure_string_list(self, option):
|
||||
r"""Ensure that 'option' is a list of strings. If 'option' is
|
||||
currently a string, we split it either on /,\s*/ or /\s+/, so
|
||||
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
|
||||
@@ -208,50 +193,13 @@ class Command(_Command):
|
||||
ok = False
|
||||
if not ok:
|
||||
raise DistutilsOptionError(
|
||||
f"'{option}' must be a list of strings (got {val!r})"
|
||||
"'%s' must be a list of strings (got %r)" % (option, val)
|
||||
)
|
||||
|
||||
@overload
|
||||
def reinitialize_command(
|
||||
self, command: str, reinit_subcommands: bool = False, **kw
|
||||
) -> _Command: ...
|
||||
@overload
|
||||
def reinitialize_command(
|
||||
self, command: _CommandT, reinit_subcommands: bool = False, **kw
|
||||
) -> _CommandT: ...
|
||||
def reinitialize_command(
|
||||
self, command: str | _Command, reinit_subcommands: bool = False, **kw
|
||||
) -> _Command:
|
||||
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
|
||||
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
|
||||
vars(cmd).update(kw)
|
||||
return cmd # pyright: ignore[reportReturnType] # pypa/distutils#307
|
||||
|
||||
@abstractmethod
|
||||
def initialize_options(self) -> None:
|
||||
"""
|
||||
Set or (reset) all options/attributes/caches used by the command
|
||||
to their default values. Note that these values may be overwritten during
|
||||
the build.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def finalize_options(self) -> None:
|
||||
"""
|
||||
Set final values for all options/attributes used by the command.
|
||||
Most of the time, each option/attribute/cache should only be set if it does not
|
||||
have any value yet (e.g. ``if self.attr is None: self.attr = val``).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def run(self) -> None:
|
||||
"""
|
||||
Execute the actions intended by the command.
|
||||
(Side effects **SHOULD** only take place when :meth:`run` is executed,
|
||||
for example, creating new files or writing to the terminal output).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
return cmd
|
||||
|
||||
|
||||
def _find_all_simple(path):
|
||||
@@ -278,6 +226,19 @@ def findall(dir=os.curdir):
|
||||
return list(files)
|
||||
|
||||
|
||||
@functools.wraps(_convert_path)
|
||||
def convert_path(pathname):
|
||||
from inspect import cleandoc
|
||||
|
||||
msg = """
|
||||
The function `convert_path` is considered internal and not part of the public API.
|
||||
Its direct usage by 3rd-party packages is considered deprecated and the function
|
||||
may be removed in the future.
|
||||
"""
|
||||
warnings.warn(cleandoc(msg), SetuptoolsDeprecationWarning)
|
||||
return _convert_path(pathname)
|
||||
|
||||
|
||||
class sic(str):
|
||||
"""Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
|
||||
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,337 +0,0 @@
|
||||
"""
|
||||
Handling of Core Metadata for Python packages (including reading and writing).
|
||||
|
||||
See: https://packaging.python.org/en/latest/specifications/core-metadata/
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import stat
|
||||
import textwrap
|
||||
from email import message_from_file
|
||||
from email.message import Message
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from packaging.markers import Marker
|
||||
from packaging.requirements import Requirement
|
||||
from packaging.utils import canonicalize_name, canonicalize_version
|
||||
from packaging.version import Version
|
||||
|
||||
from . import _normalization, _reqs
|
||||
from ._static import is_static
|
||||
from .warnings import SetuptoolsDeprecationWarning
|
||||
|
||||
from distutils.util import rfc822_escape
|
||||
|
||||
|
||||
def get_metadata_version(self):
|
||||
mv = getattr(self, 'metadata_version', None)
|
||||
if mv is None:
|
||||
mv = Version('2.4')
|
||||
self.metadata_version = mv
|
||||
return mv
|
||||
|
||||
|
||||
def rfc822_unescape(content: str) -> str:
|
||||
"""Reverse RFC-822 escaping by removing leading whitespaces from content."""
|
||||
lines = content.splitlines()
|
||||
if len(lines) == 1:
|
||||
return lines[0].lstrip()
|
||||
return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
|
||||
|
||||
|
||||
def _read_field_from_msg(msg: Message, field: str) -> str | None:
|
||||
"""Read Message header field."""
|
||||
value = msg[field]
|
||||
if value == 'UNKNOWN':
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def _read_field_unescaped_from_msg(msg: Message, field: str) -> str | None:
|
||||
"""Read Message header field and apply rfc822_unescape."""
|
||||
value = _read_field_from_msg(msg, field)
|
||||
if value is None:
|
||||
return value
|
||||
return rfc822_unescape(value)
|
||||
|
||||
|
||||
def _read_list_from_msg(msg: Message, field: str) -> list[str] | None:
|
||||
"""Read Message header field and return all results as list."""
|
||||
values = msg.get_all(field, None)
|
||||
if values == []:
|
||||
return None
|
||||
return values
|
||||
|
||||
|
||||
def _read_payload_from_msg(msg: Message) -> str | None:
|
||||
value = str(msg.get_payload()).strip()
|
||||
if value == 'UNKNOWN' or not value:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def read_pkg_file(self, file):
|
||||
"""Reads the metadata values from a file object."""
|
||||
msg = message_from_file(file)
|
||||
|
||||
self.metadata_version = Version(msg['metadata-version'])
|
||||
self.name = _read_field_from_msg(msg, 'name')
|
||||
self.version = _read_field_from_msg(msg, 'version')
|
||||
self.description = _read_field_from_msg(msg, 'summary')
|
||||
# we are filling author only.
|
||||
self.author = _read_field_from_msg(msg, 'author')
|
||||
self.maintainer = None
|
||||
self.author_email = _read_field_from_msg(msg, 'author-email')
|
||||
self.maintainer_email = None
|
||||
self.url = _read_field_from_msg(msg, 'home-page')
|
||||
self.download_url = _read_field_from_msg(msg, 'download-url')
|
||||
self.license = _read_field_unescaped_from_msg(msg, 'license')
|
||||
self.license_expression = _read_field_unescaped_from_msg(msg, 'license-expression')
|
||||
|
||||
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
|
||||
if self.long_description is None and self.metadata_version >= Version('2.1'):
|
||||
self.long_description = _read_payload_from_msg(msg)
|
||||
self.description = _read_field_from_msg(msg, 'summary')
|
||||
|
||||
if 'keywords' in msg:
|
||||
self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
|
||||
|
||||
self.platforms = _read_list_from_msg(msg, 'platform')
|
||||
self.classifiers = _read_list_from_msg(msg, 'classifier')
|
||||
|
||||
# PEP 314 - these fields only exist in 1.1
|
||||
if self.metadata_version == Version('1.1'):
|
||||
self.requires = _read_list_from_msg(msg, 'requires')
|
||||
self.provides = _read_list_from_msg(msg, 'provides')
|
||||
self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
|
||||
else:
|
||||
self.requires = None
|
||||
self.provides = None
|
||||
self.obsoletes = None
|
||||
|
||||
self.license_files = _read_list_from_msg(msg, 'license-file')
|
||||
|
||||
|
||||
def single_line(val):
|
||||
"""
|
||||
Quick and dirty validation for Summary pypa/setuptools#1390.
|
||||
"""
|
||||
if '\n' in val:
|
||||
# TODO: Replace with `raise ValueError("newlines not allowed")`
|
||||
# after reviewing #2893.
|
||||
msg = "newlines are not allowed in `summary` and will break in the future"
|
||||
SetuptoolsDeprecationWarning.emit("Invalid config.", msg)
|
||||
# due_date is undefined. Controversial change, there was a lot of push back.
|
||||
val = val.strip().split('\n')[0]
|
||||
return val
|
||||
|
||||
|
||||
def write_pkg_info(self, base_dir):
|
||||
"""Write the PKG-INFO file into the release tree."""
|
||||
temp = ""
|
||||
final = os.path.join(base_dir, 'PKG-INFO')
|
||||
try:
|
||||
# Use a temporary file while writing to avoid race conditions
|
||||
# (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`):
|
||||
with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f:
|
||||
temp = f.name
|
||||
self.write_pkg_file(f)
|
||||
permissions = stat.S_IMODE(os.lstat(temp).st_mode)
|
||||
os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH)
|
||||
os.replace(temp, final) # atomic operation.
|
||||
finally:
|
||||
if temp and os.path.exists(temp):
|
||||
os.remove(temp)
|
||||
|
||||
|
||||
# Based on Python 3.5 version
|
||||
def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
|
||||
"""Write the PKG-INFO format data to a file object."""
|
||||
version = self.get_metadata_version()
|
||||
|
||||
def write_field(key, value):
|
||||
file.write(f"{key}: {value}\n")
|
||||
|
||||
write_field('Metadata-Version', str(version))
|
||||
write_field('Name', self.get_name())
|
||||
write_field('Version', self.get_version())
|
||||
|
||||
summary = self.get_description()
|
||||
if summary:
|
||||
write_field('Summary', single_line(summary))
|
||||
|
||||
optional_fields = (
|
||||
('Home-page', 'url'),
|
||||
('Download-URL', 'download_url'),
|
||||
('Author', 'author'),
|
||||
('Author-email', 'author_email'),
|
||||
('Maintainer', 'maintainer'),
|
||||
('Maintainer-email', 'maintainer_email'),
|
||||
)
|
||||
|
||||
for field, attr in optional_fields:
|
||||
attr_val = getattr(self, attr, None)
|
||||
if attr_val is not None:
|
||||
write_field(field, attr_val)
|
||||
|
||||
if license_expression := self.license_expression:
|
||||
write_field('License-Expression', license_expression)
|
||||
elif license := self.get_license():
|
||||
write_field('License', rfc822_escape(license))
|
||||
|
||||
for label, url in self.project_urls.items():
|
||||
write_field('Project-URL', f'{label}, {url}')
|
||||
|
||||
keywords = ','.join(self.get_keywords())
|
||||
if keywords:
|
||||
write_field('Keywords', keywords)
|
||||
|
||||
platforms = self.get_platforms() or []
|
||||
for platform in platforms:
|
||||
write_field('Platform', platform)
|
||||
|
||||
self._write_list(file, 'Classifier', self.get_classifiers())
|
||||
|
||||
# PEP 314
|
||||
self._write_list(file, 'Requires', self.get_requires())
|
||||
self._write_list(file, 'Provides', self.get_provides())
|
||||
self._write_list(file, 'Obsoletes', self.get_obsoletes())
|
||||
|
||||
# Setuptools specific for PEP 345
|
||||
if hasattr(self, 'python_requires'):
|
||||
write_field('Requires-Python', self.python_requires)
|
||||
|
||||
# PEP 566
|
||||
if self.long_description_content_type:
|
||||
write_field('Description-Content-Type', self.long_description_content_type)
|
||||
|
||||
safe_license_files = map(_safe_license_file, self.license_files or [])
|
||||
self._write_list(file, 'License-File', safe_license_files)
|
||||
_write_requirements(self, file)
|
||||
|
||||
for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items():
|
||||
if (val := getattr(self, attr, None)) and not is_static(val):
|
||||
write_field('Dynamic', field)
|
||||
|
||||
long_description = self.get_long_description()
|
||||
if long_description:
|
||||
file.write(f"\n{long_description}")
|
||||
if not long_description.endswith("\n"):
|
||||
file.write("\n")
|
||||
|
||||
|
||||
def _write_requirements(self, file):
|
||||
for req in _reqs.parse(self.install_requires):
|
||||
file.write(f"Requires-Dist: {req}\n")
|
||||
|
||||
processed_extras = {}
|
||||
for augmented_extra, reqs in self.extras_require.items():
|
||||
# Historically, setuptools allows "augmented extras": `<extra>:<condition>`
|
||||
unsafe_extra, _, condition = augmented_extra.partition(":")
|
||||
unsafe_extra = unsafe_extra.strip()
|
||||
extra = _normalization.safe_extra(unsafe_extra)
|
||||
|
||||
if extra:
|
||||
_write_provides_extra(file, processed_extras, extra, unsafe_extra)
|
||||
for req in _reqs.parse_strings(reqs):
|
||||
r = _include_extra(req, extra, condition.strip())
|
||||
file.write(f"Requires-Dist: {r}\n")
|
||||
|
||||
return processed_extras
|
||||
|
||||
|
||||
def _include_extra(req: str, extra: str, condition: str) -> Requirement:
|
||||
r = Requirement(req) # create a fresh object that can be modified
|
||||
parts = (
|
||||
f"({r.marker})" if r.marker else None,
|
||||
f"({condition})" if condition else None,
|
||||
f"extra == {extra!r}" if extra else None,
|
||||
)
|
||||
r.marker = Marker(" and ".join(x for x in parts if x))
|
||||
return r
|
||||
|
||||
|
||||
def _write_provides_extra(file, processed_extras, safe, unsafe):
|
||||
previous = processed_extras.get(safe)
|
||||
if previous == unsafe:
|
||||
SetuptoolsDeprecationWarning.emit(
|
||||
'Ambiguity during "extra" normalization for dependencies.',
|
||||
f"""
|
||||
{previous!r} and {unsafe!r} normalize to the same value:\n
|
||||
{safe!r}\n
|
||||
In future versions, setuptools might halt the build process.
|
||||
""",
|
||||
see_url="https://peps.python.org/pep-0685/",
|
||||
)
|
||||
else:
|
||||
processed_extras[safe] = unsafe
|
||||
file.write(f"Provides-Extra: {safe}\n")
|
||||
|
||||
|
||||
# from pypa/distutils#244; needed only until that logic is always available
|
||||
def get_fullname(self):
|
||||
return _distribution_fullname(self.get_name(), self.get_version())
|
||||
|
||||
|
||||
def _distribution_fullname(name: str, version: str) -> str:
|
||||
"""
|
||||
>>> _distribution_fullname('setup.tools', '1.0-2')
|
||||
'setup_tools-1.0.post2'
|
||||
>>> _distribution_fullname('setup-tools', '1.2post2')
|
||||
'setup_tools-1.2.post2'
|
||||
>>> _distribution_fullname('setup-tools', '1.0-r2')
|
||||
'setup_tools-1.0.post2'
|
||||
>>> _distribution_fullname('setup.tools', '1.0.post')
|
||||
'setup_tools-1.0.post0'
|
||||
>>> _distribution_fullname('setup.tools', '1.0+ubuntu-1')
|
||||
'setup_tools-1.0+ubuntu.1'
|
||||
"""
|
||||
return "{}-{}".format(
|
||||
canonicalize_name(name).replace('-', '_'),
|
||||
canonicalize_version(version, strip_trailing_zero=False),
|
||||
)
|
||||
|
||||
|
||||
def _safe_license_file(file):
|
||||
# XXX: Do we need this after the deprecation discussed in #4892, #4896??
|
||||
normalized = os.path.normpath(file).replace(os.sep, "/")
|
||||
if "../" in normalized:
|
||||
return os.path.basename(normalized) # Temporarily restore pre PEP639 behaviour
|
||||
return normalized
|
||||
|
||||
|
||||
_POSSIBLE_DYNAMIC_FIELDS = {
|
||||
# Core Metadata Field x related Distribution attribute
|
||||
"author": "author",
|
||||
"author-email": "author_email",
|
||||
"classifier": "classifiers",
|
||||
"description": "long_description",
|
||||
"description-content-type": "long_description_content_type",
|
||||
"download-url": "download_url",
|
||||
"home-page": "url",
|
||||
"keywords": "keywords",
|
||||
"license": "license",
|
||||
# XXX: License-File is complicated because the user gives globs that are expanded
|
||||
# during the build. Without special handling it is likely always
|
||||
# marked as Dynamic, which is an acceptable outcome according to:
|
||||
# https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677
|
||||
"license-file": "license_files",
|
||||
"license-expression": "license_expression", # PEP 639
|
||||
"maintainer": "maintainer",
|
||||
"maintainer-email": "maintainer_email",
|
||||
"obsoletes": "obsoletes",
|
||||
# "obsoletes-dist": "obsoletes_dist", # NOT USED
|
||||
"platform": "platforms",
|
||||
"project-url": "project_urls",
|
||||
"provides": "provides",
|
||||
# "provides-dist": "provides_dist", # NOT USED
|
||||
"provides-extra": "extras_require",
|
||||
"requires": "requires",
|
||||
"requires-dist": "install_requires",
|
||||
# "requires-external": "requires_external", # NOT USED
|
||||
"requires-python": "python_requires",
|
||||
"summary": "description",
|
||||
# "supported-platform": "supported_platforms", # NOT USED
|
||||
}
|
||||
@@ -1,7 +1,17 @@
|
||||
import importlib
|
||||
import sys
|
||||
"""distutils
|
||||
|
||||
__version__, _, _ = sys.version.partition(' ')
|
||||
The main package for the Python Module Distribution Utilities. Normally
|
||||
used from a setup script as
|
||||
|
||||
from distutils.core import setup
|
||||
|
||||
setup (...)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import importlib
|
||||
|
||||
__version__ = sys.version[: sys.version.index(' ')]
|
||||
|
||||
|
||||
try:
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
import logging
|
||||
|
||||
log = logging.getLogger()
|
||||
@@ -1,5 +1,5 @@
|
||||
import importlib
|
||||
import sys
|
||||
import importlib
|
||||
|
||||
|
||||
def bypass_compiler_fixup(cmd, args):
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
"""Timestamp comparison of files and groups of files."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import os.path
|
||||
from collections.abc import Callable, Iterable
|
||||
from typing import Literal, TypeVar
|
||||
|
||||
from jaraco.functools import splat
|
||||
|
||||
from .compat.py39 import zip_strict
|
||||
from .errors import DistutilsFileError
|
||||
|
||||
_SourcesT = TypeVar(
|
||||
"_SourcesT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
|
||||
)
|
||||
_TargetsT = TypeVar(
|
||||
"_TargetsT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
|
||||
)
|
||||
|
||||
|
||||
def _newer(source, target):
|
||||
return not os.path.exists(target) or (
|
||||
os.path.getmtime(source) > os.path.getmtime(target)
|
||||
)
|
||||
|
||||
|
||||
def newer(
|
||||
source: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
||||
target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
||||
) -> bool:
|
||||
"""
|
||||
Is source modified more recently than target.
|
||||
|
||||
Returns True if 'source' is modified more recently than
|
||||
'target' or if 'target' does not exist.
|
||||
|
||||
Raises DistutilsFileError if 'source' does not exist.
|
||||
"""
|
||||
if not os.path.exists(source):
|
||||
raise DistutilsFileError(f"file {os.path.abspath(source)!r} does not exist")
|
||||
|
||||
return _newer(source, target)
|
||||
|
||||
|
||||
def newer_pairwise(
|
||||
sources: Iterable[_SourcesT],
|
||||
targets: Iterable[_TargetsT],
|
||||
newer: Callable[[_SourcesT, _TargetsT], bool] = newer,
|
||||
) -> tuple[list[_SourcesT], list[_TargetsT]]:
|
||||
"""
|
||||
Filter filenames where sources are newer than targets.
|
||||
|
||||
Walk two filename iterables in parallel, testing if each source is newer
|
||||
than its corresponding target. Returns a pair of lists (sources,
|
||||
targets) where source is newer than target, according to the semantics
|
||||
of 'newer()'.
|
||||
"""
|
||||
newer_pairs = filter(splat(newer), zip_strict(sources, targets))
|
||||
return tuple(map(list, zip(*newer_pairs))) or ([], [])
|
||||
|
||||
|
||||
def newer_group(
|
||||
sources: Iterable[str | bytes | os.PathLike[str] | os.PathLike[bytes]],
|
||||
target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
|
||||
missing: Literal["error", "ignore", "newer"] = "error",
|
||||
) -> bool:
|
||||
"""
|
||||
Is target out-of-date with respect to any file in sources.
|
||||
|
||||
Return True if 'target' is out-of-date with respect to any file
|
||||
listed in 'sources'. In other words, if 'target' exists and is newer
|
||||
than every file in 'sources', return False; otherwise return True.
|
||||
``missing`` controls how to handle a missing source file:
|
||||
|
||||
- error (default): allow the ``stat()`` call to fail.
|
||||
- ignore: silently disregard any missing source files.
|
||||
- newer: treat missing source files as "target out of date". This
|
||||
mode is handy in "dry-run" mode: it will pretend to carry out
|
||||
commands that wouldn't work because inputs are missing, but
|
||||
that doesn't matter because dry-run won't run the commands.
|
||||
"""
|
||||
|
||||
def missing_as_newer(source):
|
||||
return missing == 'newer' and not os.path.exists(source)
|
||||
|
||||
ignored = os.path.exists if missing == 'ignore' else None
|
||||
return not os.path.exists(target) or any(
|
||||
missing_as_newer(source) or _newer(source, target)
|
||||
for source in filter(ignored, sources)
|
||||
)
|
||||
|
||||
|
||||
newer_pairwise_group = functools.partial(newer_pairwise, newer=newer_group)
|
||||
@@ -1,16 +1,572 @@
|
||||
"""distutils._msvccompiler
|
||||
|
||||
Contains MSVCCompiler, an implementation of the abstract CCompiler class
|
||||
for Microsoft Visual Studio 2015.
|
||||
|
||||
The module is compatible with VS 2015 and later. You can find legacy support
|
||||
for older versions in distutils.msvc9compiler and 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 VS 2005 and VS 2008 by Christian Heimes
|
||||
# ported to VS 2015 by Steve Dower
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import contextlib
|
||||
import warnings
|
||||
import unittest.mock as mock
|
||||
|
||||
from .compilers.C import msvc
|
||||
with contextlib.suppress(ImportError):
|
||||
import winreg
|
||||
|
||||
__all__ = ["MSVCCompiler"]
|
||||
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
|
||||
|
||||
MSVCCompiler = msvc.Compiler
|
||||
from itertools import count
|
||||
|
||||
|
||||
def __getattr__(name):
|
||||
if name == '_get_vc_env':
|
||||
warnings.warn(
|
||||
"_get_vc_env is private; find an alternative (pypa/distutils#340)"
|
||||
def _find_vc2015():
|
||||
try:
|
||||
key = winreg.OpenKeyEx(
|
||||
winreg.HKEY_LOCAL_MACHINE,
|
||||
r"Software\Microsoft\VisualStudio\SxS\VC7",
|
||||
access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
|
||||
)
|
||||
return msvc._get_vc_env
|
||||
raise AttributeError(name)
|
||||
except OSError:
|
||||
log.debug("Visual C++ is not registered")
|
||||
return None, None
|
||||
|
||||
best_version = 0
|
||||
best_dir = None
|
||||
with key:
|
||||
for i in count():
|
||||
try:
|
||||
v, vc_dir, vt = winreg.EnumValue(key, i)
|
||||
except OSError:
|
||||
break
|
||||
if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
|
||||
try:
|
||||
version = int(float(v))
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
if version >= 14 and version > best_version:
|
||||
best_version, best_dir = version, vc_dir
|
||||
return best_version, best_dir
|
||||
|
||||
|
||||
def _find_vc2017():
|
||||
"""Returns "15, path" based on the result of invoking vswhere.exe
|
||||
If no install is found, returns "None, None"
|
||||
|
||||
The version is returned to avoid unnecessarily changing the function
|
||||
result. It may be ignored when the path is not None.
|
||||
|
||||
If vswhere.exe is not available, by definition, VS 2017 is not
|
||||
installed.
|
||||
"""
|
||||
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
|
||||
if not root:
|
||||
return None, None
|
||||
|
||||
try:
|
||||
path = subprocess.check_output(
|
||||
[
|
||||
os.path.join(
|
||||
root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
|
||||
),
|
||||
"-latest",
|
||||
"-prerelease",
|
||||
"-requires",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"-property",
|
||||
"installationPath",
|
||||
"-products",
|
||||
"*",
|
||||
],
|
||||
encoding="mbcs",
|
||||
errors="strict",
|
||||
).strip()
|
||||
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
|
||||
return None, None
|
||||
|
||||
path = os.path.join(path, "VC", "Auxiliary", "Build")
|
||||
if os.path.isdir(path):
|
||||
return 15, path
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
PLAT_SPEC_TO_RUNTIME = {
|
||||
'x86': 'x86',
|
||||
'x86_amd64': 'x64',
|
||||
'x86_arm': 'arm',
|
||||
'x86_arm64': 'arm64',
|
||||
}
|
||||
|
||||
|
||||
def _find_vcvarsall(plat_spec):
|
||||
# bpo-38597: Removed vcruntime return value
|
||||
_, best_dir = _find_vc2017()
|
||||
|
||||
if not best_dir:
|
||||
best_version, best_dir = _find_vc2015()
|
||||
|
||||
if not best_dir:
|
||||
log.debug("No suitable Visual C++ version found")
|
||||
return None, None
|
||||
|
||||
vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
|
||||
if not os.path.isfile(vcvarsall):
|
||||
log.debug("%s cannot be found", vcvarsall)
|
||||
return None, None
|
||||
|
||||
return vcvarsall, None
|
||||
|
||||
|
||||
def _get_vc_env(plat_spec):
|
||||
if os.getenv("DISTUTILS_USE_SDK"):
|
||||
return {key.lower(): value for key, value in os.environ.items()}
|
||||
|
||||
vcvarsall, _ = _find_vcvarsall(plat_spec)
|
||||
if not vcvarsall:
|
||||
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
|
||||
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
|
||||
stderr=subprocess.STDOUT,
|
||||
).decode('utf-16le', errors='replace')
|
||||
except subprocess.CalledProcessError as exc:
|
||||
log.error(exc.output)
|
||||
raise DistutilsPlatformError(f"Error executing {exc.cmd}")
|
||||
|
||||
env = {
|
||||
key.lower(): value
|
||||
for key, _, value in (line.partition('=') for line in out.splitlines())
|
||||
if key and value
|
||||
}
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def _find_exe(exe, paths=None):
|
||||
"""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'.
|
||||
"""
|
||||
if not paths:
|
||||
paths = os.getenv('path').split(os.pathsep)
|
||||
for p in paths:
|
||||
fn = os.path.join(os.path.abspath(p), exe)
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
return exe
|
||||
|
||||
|
||||
# A map keyed by get_platform() return values to values accepted by
|
||||
# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
|
||||
# lighter-weight MSVC installs that do not include native 64-bit tools.
|
||||
PLAT_TO_VCVARS = {
|
||||
'win32': 'x86',
|
||||
'win-amd64': 'x86_amd64',
|
||||
'win-arm32': 'x86_arm',
|
||||
'win-arm64': 'x86_arm64',
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
# target platform (.plat_name is consistent with 'bdist')
|
||||
self.plat_name = None
|
||||
self.initialized = False
|
||||
|
||||
@classmethod
|
||||
def _configure(cls, vc_env):
|
||||
"""
|
||||
Set class-level include/lib dirs.
|
||||
"""
|
||||
cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
|
||||
cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
|
||||
|
||||
@staticmethod
|
||||
def _parse_path(val):
|
||||
return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
|
||||
|
||||
def initialize(self, plat_name=None):
|
||||
# multi-init means we would need to check platform same each time...
|
||||
assert not self.initialized, "don't init multiple times"
|
||||
if plat_name is None:
|
||||
plat_name = get_platform()
|
||||
# sanity check for platforms to prevent obscure errors later.
|
||||
if plat_name not in PLAT_TO_VCVARS:
|
||||
raise DistutilsPlatformError(
|
||||
f"--plat-name must be one of {tuple(PLAT_TO_VCVARS)}"
|
||||
)
|
||||
|
||||
# Get the vcvarsall.bat spec for the requested platform.
|
||||
plat_spec = PLAT_TO_VCVARS[plat_name]
|
||||
|
||||
vc_env = _get_vc_env(plat_spec)
|
||||
if not vc_env:
|
||||
raise DistutilsPlatformError(
|
||||
"Unable to find a compatible " "Visual Studio installation."
|
||||
)
|
||||
self._configure(vc_env)
|
||||
|
||||
self._paths = vc_env.get('path', '')
|
||||
paths = self._paths.split(os.pathsep)
|
||||
self.cc = _find_exe("cl.exe", paths)
|
||||
self.linker = _find_exe("link.exe", paths)
|
||||
self.lib = _find_exe("lib.exe", paths)
|
||||
self.rc = _find_exe("rc.exe", paths) # resource compiler
|
||||
self.mc = _find_exe("mc.exe", paths) # message compiler
|
||||
self.mt = _find_exe("mt.exe", paths) # message compiler
|
||||
|
||||
self.preprocess_options = None
|
||||
# bpo-38597: Always compile with dynamic linking
|
||||
# Future releases of Python 3.x will include all past
|
||||
# versions of vcruntime*.dll for compatibility.
|
||||
self.compile_options = ['/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD']
|
||||
|
||||
self.compile_options_debug = [
|
||||
'/nologo',
|
||||
'/Od',
|
||||
'/MDd',
|
||||
'/Zi',
|
||||
'/W3',
|
||||
'/D_DEBUG',
|
||||
]
|
||||
|
||||
ldflags = ['/nologo', '/INCREMENTAL:NO', '/LTCG']
|
||||
|
||||
ldflags_debug = ['/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL']
|
||||
|
||||
self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
|
||||
self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
|
||||
self.ldflags_shared = [
|
||||
*ldflags,
|
||||
'/DLL',
|
||||
'/MANIFEST:EMBED,ID=2',
|
||||
'/MANIFESTUAC:NO',
|
||||
]
|
||||
self.ldflags_shared_debug = [
|
||||
*ldflags_debug,
|
||||
'/DLL',
|
||||
'/MANIFEST:EMBED,ID=2',
|
||||
'/MANIFESTUAC:NO',
|
||||
]
|
||||
self.ldflags_static = [*ldflags]
|
||||
self.ldflags_static_debug = [*ldflags_debug]
|
||||
|
||||
self._ldflags = {
|
||||
(CCompiler.EXECUTABLE, None): self.ldflags_exe,
|
||||
(CCompiler.EXECUTABLE, False): self.ldflags_exe,
|
||||
(CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
|
||||
(CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
|
||||
(CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
|
||||
(CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
|
||||
(CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
|
||||
(CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
|
||||
(CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
|
||||
}
|
||||
|
||||
self.initialized = True
|
||||
|
||||
# -- Worker methods ------------------------------------------------
|
||||
|
||||
@property
|
||||
def out_extensions(self):
|
||||
return {
|
||||
**super().out_extensions,
|
||||
**{
|
||||
ext: self.res_extension
|
||||
for ext in self._rc_extensions + self._mc_extensions
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
add_cpp_opts = False
|
||||
|
||||
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
|
||||
add_cpp_opts = True
|
||||
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(f"Don't know how to compile {src} to {obj}")
|
||||
|
||||
args = [self.cc] + compile_opts + pp_opts
|
||||
if add_cpp_opts:
|
||||
args.append('/EHsc')
|
||||
args.append(input_opt)
|
||||
args.append("/Fo" + obj)
|
||||
args.extend(extra_postargs)
|
||||
|
||||
try:
|
||||
self.spawn(args)
|
||||
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:
|
||||
log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
|
||||
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(
|
||||
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):
|
||||
ldflags = self._ldflags[target_desc, debug]
|
||||
|
||||
export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
|
||||
|
||||
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)
|
||||
|
||||
if extra_preargs:
|
||||
ld_args[:0] = extra_preargs
|
||||
if extra_postargs:
|
||||
ld_args.extend(extra_postargs)
|
||||
|
||||
output_dir = os.path.dirname(os.path.abspath(output_filename))
|
||||
self.mkpath(output_dir)
|
||||
try:
|
||||
log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
|
||||
self.spawn([self.linker] + ld_args)
|
||||
except DistutilsExecError as msg:
|
||||
raise LinkError(msg)
|
||||
else:
|
||||
log.debug("skipping %s (up-to-date)", output_filename)
|
||||
|
||||
def spawn(self, cmd):
|
||||
env = dict(os.environ, PATH=self._paths)
|
||||
with self._fallback_spawn(cmd, env) as fallback:
|
||||
return super().spawn(cmd, env=env)
|
||||
return fallback.value
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _fallback_spawn(self, cmd, env):
|
||||
"""
|
||||
Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
|
||||
so the 'env' kwarg causes a TypeError. Detect this condition and
|
||||
restore the legacy, unsafe behavior.
|
||||
"""
|
||||
bag = type('Bag', (), {})()
|
||||
try:
|
||||
yield bag
|
||||
except TypeError as exc:
|
||||
if "unexpected keyword argument 'env'" not in str(exc):
|
||||
raise
|
||||
else:
|
||||
return
|
||||
warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
|
||||
with mock.patch.dict('os.environ', env):
|
||||
bag.value = super().spawn(cmd)
|
||||
|
||||
# -- 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.isfile(libfile):
|
||||
return libfile
|
||||
else:
|
||||
# Oops, didn't find it in *any* of 'dirs'
|
||||
return None
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
Utility functions for creating archive files (tarballs, zip files,
|
||||
that sort of thing)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Literal, overload
|
||||
from warnings import warn
|
||||
import sys
|
||||
|
||||
try:
|
||||
import zipfile
|
||||
@@ -14,10 +13,10 @@ except ImportError:
|
||||
zipfile = None
|
||||
|
||||
|
||||
from ._log import log
|
||||
from .dir_util import mkpath
|
||||
from .errors import DistutilsExecError
|
||||
from .spawn import spawn
|
||||
from distutils.errors import DistutilsExecError
|
||||
from distutils.spawn import spawn
|
||||
from distutils.dir_util import mkpath
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from pwd import getpwnam
|
||||
@@ -57,18 +56,13 @@ def _get_uid(name):
|
||||
|
||||
|
||||
def make_tarball(
|
||||
base_name: str,
|
||||
base_dir: str | os.PathLike[str],
|
||||
compress: Literal["gzip", "bzip2", "xz"] | None = "gzip",
|
||||
verbose: bool = False,
|
||||
dry_run: bool = False,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str:
|
||||
base_name, base_dir, compress="gzip", verbose=0, dry_run=0, owner=None, group=None
|
||||
):
|
||||
"""Create a (possibly compressed) tar file from all the files under
|
||||
'base_dir'.
|
||||
|
||||
'compress' must be "gzip" (the default), "bzip2", "xz", or None.
|
||||
'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
|
||||
None. ("compress" will be deprecated in Python 3.2)
|
||||
|
||||
'owner' and 'group' can be used to define an owner and a group for the
|
||||
archive that is being built. If not provided, the current owner and group
|
||||
@@ -84,17 +78,20 @@ def make_tarball(
|
||||
'bzip2': 'bz2',
|
||||
'xz': 'xz',
|
||||
None: '',
|
||||
'compress': '',
|
||||
}
|
||||
compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz'}
|
||||
compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
|
||||
|
||||
# flags for compression program, each element of list will be an argument
|
||||
if compress is not None and compress not in compress_ext.keys():
|
||||
raise ValueError(
|
||||
"bad value for 'compress': must be None, 'gzip', 'bzip2', 'xz'"
|
||||
"bad value for 'compress': must be None, 'gzip', 'bzip2', "
|
||||
"'xz' or 'compress'"
|
||||
)
|
||||
|
||||
archive_name = base_name + '.tar'
|
||||
archive_name += compress_ext.get(compress, '')
|
||||
if compress != 'compress':
|
||||
archive_name += compress_ext.get(compress, '')
|
||||
|
||||
mkpath(os.path.dirname(archive_name), dry_run=dry_run)
|
||||
|
||||
@@ -116,21 +113,28 @@ def make_tarball(
|
||||
return tarinfo
|
||||
|
||||
if not dry_run:
|
||||
tar = tarfile.open(archive_name, f'w|{tar_compression[compress]}')
|
||||
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
|
||||
try:
|
||||
tar.add(base_dir, filter=_set_uid_gid)
|
||||
finally:
|
||||
tar.close()
|
||||
|
||||
# compression using `compress`
|
||||
if compress == 'compress':
|
||||
warn("'compress' is deprecated.", DeprecationWarning)
|
||||
# the option varies depending on the platform
|
||||
compressed_name = archive_name + compress_ext[compress]
|
||||
if sys.platform == 'win32':
|
||||
cmd = [compress, archive_name, compressed_name]
|
||||
else:
|
||||
cmd = [compress, '-f', archive_name]
|
||||
spawn(cmd, dry_run=dry_run)
|
||||
return compressed_name
|
||||
|
||||
return archive_name
|
||||
|
||||
|
||||
def make_zipfile( # noqa: C901
|
||||
base_name: str,
|
||||
base_dir: str | os.PathLike[str],
|
||||
verbose: bool = False,
|
||||
dry_run: bool = False,
|
||||
) -> str:
|
||||
def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): # noqa: C901
|
||||
"""Create a zip file from all the files under 'base_dir'.
|
||||
|
||||
The output zip file will be named 'base_name' + ".zip". Uses either the
|
||||
@@ -156,9 +160,12 @@ def make_zipfile( # noqa: C901
|
||||
# XXX really should distinguish between "couldn't find
|
||||
# external 'zip' command" and "zip failed".
|
||||
raise DistutilsExecError(
|
||||
f"unable to create zip file '{zip_filename}': "
|
||||
"could neither import the 'zipfile' module nor "
|
||||
"find a standalone zip utility"
|
||||
(
|
||||
"unable to create zip file '%s': "
|
||||
"could neither import the 'zipfile' module nor "
|
||||
"find a standalone zip utility"
|
||||
)
|
||||
% zip_filename
|
||||
)
|
||||
|
||||
else:
|
||||
@@ -212,38 +219,16 @@ def check_archive_formats(formats):
|
||||
return None
|
||||
|
||||
|
||||
@overload
|
||||
def make_archive(
|
||||
base_name: str,
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
|
||||
base_dir: str | None = None,
|
||||
verbose: bool = False,
|
||||
dry_run: bool = False,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str: ...
|
||||
@overload
|
||||
def make_archive(
|
||||
base_name: str | os.PathLike[str],
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
base_dir: str | None = None,
|
||||
verbose: bool = False,
|
||||
dry_run: bool = False,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str: ...
|
||||
def make_archive(
|
||||
base_name: str | os.PathLike[str],
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
|
||||
base_dir: str | None = None,
|
||||
verbose: bool = False,
|
||||
dry_run: bool = False,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str:
|
||||
base_name,
|
||||
format,
|
||||
root_dir=None,
|
||||
base_dir=None,
|
||||
verbose=0,
|
||||
dry_run=0,
|
||||
owner=None,
|
||||
group=None,
|
||||
):
|
||||
"""Create an archive file (eg. zip or tar).
|
||||
|
||||
'base_name' is the name of the file to create, minus any format-specific
|
||||
@@ -275,10 +260,11 @@ def make_archive(
|
||||
try:
|
||||
format_info = ARCHIVE_FORMATS[format]
|
||||
except KeyError:
|
||||
raise ValueError(f"unknown archive format '{format}'")
|
||||
raise ValueError("unknown archive format '%s'" % format)
|
||||
|
||||
func = format_info[0]
|
||||
kwargs.update(format_info[1])
|
||||
for arg, val in format_info[1]:
|
||||
kwargs[arg] = val
|
||||
|
||||
if format != 'zip':
|
||||
kwargs['owner'] = owner
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,31 +4,12 @@ Provides the Command class, the base class for the command classes
|
||||
in the distutils.command package.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, MutableSequence
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
|
||||
|
||||
from . import _modified, archive_util, dir_util, file_util, util
|
||||
from ._log import log
|
||||
from .errors import DistutilsOptionError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# type-only import because of mutual dependence between these classes
|
||||
from distutils.dist import Distribution
|
||||
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
_Ts = TypeVarTuple("_Ts")
|
||||
|
||||
_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
|
||||
_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
|
||||
_CommandT = TypeVar("_CommandT", bound="Command")
|
||||
from distutils.errors import DistutilsOptionError
|
||||
from distutils import util, dir_util, file_util, archive_util, dep_util
|
||||
from distutils import log
|
||||
|
||||
|
||||
class Command:
|
||||
@@ -61,18 +42,11 @@ class Command:
|
||||
# 'sub_commands' is usually defined at the *end* of a class, because
|
||||
# predicates can be unbound methods, so they must already have been
|
||||
# defined. The canonical example is the "install" command.
|
||||
sub_commands: ClassVar[ # Any to work around variance issues
|
||||
list[tuple[str, Callable[[Any], bool] | None]]
|
||||
] = []
|
||||
|
||||
user_options: ClassVar[
|
||||
# Specifying both because list is invariant. Avoids mypy override assignment issues
|
||||
list[tuple[str, str, str]] | list[tuple[str, str | None, str]]
|
||||
] = []
|
||||
sub_commands = []
|
||||
|
||||
# -- Creation/initialization methods -------------------------------
|
||||
|
||||
def __init__(self, dist: Distribution) -> None:
|
||||
def __init__(self, dist):
|
||||
"""Create and initialize a new Command object. Most importantly,
|
||||
invokes the 'initialize_options()' method, which is the real
|
||||
initializer and depends on the actual command being
|
||||
@@ -111,13 +85,13 @@ class Command:
|
||||
|
||||
# The 'help' flag is just used for command-line parsing, so
|
||||
# none of that complicated bureaucracy is needed.
|
||||
self.help = False
|
||||
self.help = 0
|
||||
|
||||
# 'finalized' records whether or not 'finalize_options()' has been
|
||||
# called. 'finalize_options()' itself should not pay attention to
|
||||
# this flag: it is the business of 'ensure_finalized()', which
|
||||
# always calls 'finalize_options()', to respect/update it.
|
||||
self.finalized = False
|
||||
self.finalized = 0
|
||||
|
||||
# XXX A more explicit way to customize dry_run would be better.
|
||||
def __getattr__(self, attr):
|
||||
@@ -130,10 +104,10 @@ class Command:
|
||||
else:
|
||||
raise AttributeError(attr)
|
||||
|
||||
def ensure_finalized(self) -> None:
|
||||
def ensure_finalized(self):
|
||||
if not self.finalized:
|
||||
self.finalize_options()
|
||||
self.finalized = True
|
||||
self.finalized = 1
|
||||
|
||||
# Subclasses must define:
|
||||
# initialize_options()
|
||||
@@ -148,8 +122,7 @@ class Command:
|
||||
# run the command: do whatever it is we're here to do,
|
||||
# controlled by the command's various option values
|
||||
|
||||
@abstractmethod
|
||||
def initialize_options(self) -> None:
|
||||
def initialize_options(self):
|
||||
"""Set default values for all the options that this command
|
||||
supports. Note that these defaults may be overridden by other
|
||||
commands, by the setup script, by config files, or by the
|
||||
@@ -160,11 +133,10 @@ class Command:
|
||||
This method must be implemented by all command classes.
|
||||
"""
|
||||
raise RuntimeError(
|
||||
f"abstract method -- subclass {self.__class__} must override"
|
||||
"abstract method -- subclass %s must override" % self.__class__
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def finalize_options(self) -> None:
|
||||
def finalize_options(self):
|
||||
"""Set final values for all the options that this command supports.
|
||||
This is always called as late as possible, ie. after any option
|
||||
assignments from the command-line or from other commands have been
|
||||
@@ -176,25 +148,24 @@ class Command:
|
||||
This method must be implemented by all command classes.
|
||||
"""
|
||||
raise RuntimeError(
|
||||
f"abstract method -- subclass {self.__class__} must override"
|
||||
"abstract method -- subclass %s must override" % self.__class__
|
||||
)
|
||||
|
||||
def dump_options(self, header=None, indent=""):
|
||||
from distutils.fancy_getopt import longopt_xlate
|
||||
|
||||
if header is None:
|
||||
header = f"command options for '{self.get_command_name()}':"
|
||||
self.announce(indent + header, level=logging.INFO)
|
||||
header = "command options for '%s':" % self.get_command_name()
|
||||
self.announce(indent + header, level=log.INFO)
|
||||
indent = indent + " "
|
||||
for option, _, _ in self.user_options:
|
||||
for (option, _, _) in self.user_options:
|
||||
option = option.translate(longopt_xlate)
|
||||
if option[-1] == "=":
|
||||
option = option[:-1]
|
||||
value = getattr(self, option)
|
||||
self.announce(indent + f"{option} = {value}", level=logging.INFO)
|
||||
self.announce(indent + "{} = {}".format(option, value), level=log.INFO)
|
||||
|
||||
@abstractmethod
|
||||
def run(self) -> None:
|
||||
def run(self):
|
||||
"""A command's raison d'etre: carry out the action it exists to
|
||||
perform, controlled by the options initialized in
|
||||
'initialize_options()', customized by other commands, the setup
|
||||
@@ -205,13 +176,16 @@ class Command:
|
||||
This method must be implemented by all command classes.
|
||||
"""
|
||||
raise RuntimeError(
|
||||
f"abstract method -- subclass {self.__class__} must override"
|
||||
"abstract method -- subclass %s must override" % self.__class__
|
||||
)
|
||||
|
||||
def announce(self, msg: object, level: int = logging.DEBUG) -> None:
|
||||
def announce(self, msg, level=1):
|
||||
"""If the current verbosity level is of greater than or equal to
|
||||
'level' print 'msg' to stdout.
|
||||
"""
|
||||
log.log(level, msg)
|
||||
|
||||
def debug_print(self, msg: object) -> None:
|
||||
def debug_print(self, msg):
|
||||
"""Print 'msg' to stdout if the global DEBUG (taken from the
|
||||
DISTUTILS_DEBUG environment variable) flag is true.
|
||||
"""
|
||||
@@ -240,16 +214,18 @@ class Command:
|
||||
setattr(self, option, default)
|
||||
return default
|
||||
elif not isinstance(val, str):
|
||||
raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
|
||||
raise DistutilsOptionError(
|
||||
"'{}' must be a {} (got `{}`)".format(option, what, val)
|
||||
)
|
||||
return val
|
||||
|
||||
def ensure_string(self, option: str, default: str | None = None) -> None:
|
||||
def ensure_string(self, option, default=None):
|
||||
"""Ensure that 'option' is a string; if not defined, set it to
|
||||
'default'.
|
||||
"""
|
||||
self._ensure_stringlike(option, "string", default)
|
||||
|
||||
def ensure_string_list(self, option: str) -> None:
|
||||
def ensure_string_list(self, option):
|
||||
r"""Ensure that 'option' is a list of strings. If 'option' is
|
||||
currently a string, we split it either on /,\s*/ or /\s+/, so
|
||||
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
|
||||
@@ -267,7 +243,7 @@ class Command:
|
||||
ok = False
|
||||
if not ok:
|
||||
raise DistutilsOptionError(
|
||||
f"'{option}' must be a list of strings (got {val!r})"
|
||||
"'{}' must be a list of strings (got {!r})".format(option, val)
|
||||
)
|
||||
|
||||
def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
|
||||
@@ -277,13 +253,13 @@ class Command:
|
||||
("error in '%s' option: " + error_fmt) % (option, val)
|
||||
)
|
||||
|
||||
def ensure_filename(self, option: str) -> None:
|
||||
def ensure_filename(self, option):
|
||||
"""Ensure that 'option' is the name of an existing file."""
|
||||
self._ensure_tested_string(
|
||||
option, os.path.isfile, "filename", "'%s' does not exist or is not a file"
|
||||
)
|
||||
|
||||
def ensure_dirname(self, option: str) -> None:
|
||||
def ensure_dirname(self, option):
|
||||
self._ensure_tested_string(
|
||||
option,
|
||||
os.path.isdir,
|
||||
@@ -293,15 +269,13 @@ class Command:
|
||||
|
||||
# -- Convenience methods for commands ------------------------------
|
||||
|
||||
def get_command_name(self) -> str:
|
||||
def get_command_name(self):
|
||||
if hasattr(self, 'command_name'):
|
||||
return self.command_name
|
||||
else:
|
||||
return self.__class__.__name__
|
||||
|
||||
def set_undefined_options(
|
||||
self, src_cmd: str, *option_pairs: tuple[str, str]
|
||||
) -> None:
|
||||
def set_undefined_options(self, src_cmd, *option_pairs):
|
||||
"""Set the values of any "undefined" options from corresponding
|
||||
option values in some other command object. "Undefined" here means
|
||||
"is None", which is the convention used to indicate that an option
|
||||
@@ -318,13 +292,11 @@ class Command:
|
||||
# Option_pairs: list of (src_option, dst_option) tuples
|
||||
src_cmd_obj = self.distribution.get_command_obj(src_cmd)
|
||||
src_cmd_obj.ensure_finalized()
|
||||
for src_option, dst_option in option_pairs:
|
||||
for (src_option, dst_option) in option_pairs:
|
||||
if getattr(self, dst_option) is None:
|
||||
setattr(self, dst_option, getattr(src_cmd_obj, src_option))
|
||||
|
||||
# NOTE: Because distutils is private to Setuptools and not all commands are exposed here,
|
||||
# not every possible command is enumerated in the signature.
|
||||
def get_finalized_command(self, command: str, create: bool = True) -> Command:
|
||||
def get_finalized_command(self, command, create=1):
|
||||
"""Wrapper around Distribution's 'get_command_obj()' method: find
|
||||
(create if necessary and 'create' is true) the command object for
|
||||
'command', call its 'ensure_finalized()' method, and return the
|
||||
@@ -336,27 +308,17 @@ class Command:
|
||||
|
||||
# XXX rename to 'get_reinitialized_command()'? (should do the
|
||||
# same in dist.py, if so)
|
||||
@overload
|
||||
def reinitialize_command(
|
||||
self, command: str, reinit_subcommands: bool = False
|
||||
) -> Command: ...
|
||||
@overload
|
||||
def reinitialize_command(
|
||||
self, command: _CommandT, reinit_subcommands: bool = False
|
||||
) -> _CommandT: ...
|
||||
def reinitialize_command(
|
||||
self, command: str | Command, reinit_subcommands=False
|
||||
) -> Command:
|
||||
def reinitialize_command(self, command, reinit_subcommands=0):
|
||||
return self.distribution.reinitialize_command(command, reinit_subcommands)
|
||||
|
||||
def run_command(self, command: str) -> None:
|
||||
def run_command(self, command):
|
||||
"""Run some other command: uses the 'run_command()' method of
|
||||
Distribution, which creates and finalizes the command object if
|
||||
necessary and then invokes its 'run()' method.
|
||||
"""
|
||||
self.distribution.run_command(command)
|
||||
|
||||
def get_sub_commands(self) -> list[str]:
|
||||
def get_sub_commands(self):
|
||||
"""Determine the sub-commands that are relevant in the current
|
||||
distribution (ie., that need to be run). This is based on the
|
||||
'sub_commands' class attribute: each tuple in that list may include
|
||||
@@ -364,57 +326,25 @@ class Command:
|
||||
run for the current distribution. Return a list of command names.
|
||||
"""
|
||||
commands = []
|
||||
for cmd_name, method in self.sub_commands:
|
||||
for (cmd_name, method) in self.sub_commands:
|
||||
if method is None or method(self):
|
||||
commands.append(cmd_name)
|
||||
return commands
|
||||
|
||||
# -- External world manipulation -----------------------------------
|
||||
|
||||
def warn(self, msg: object) -> None:
|
||||
log.warning("warning: %s: %s\n", self.get_command_name(), msg)
|
||||
def warn(self, msg):
|
||||
log.warn("warning: %s: %s\n", self.get_command_name(), msg)
|
||||
|
||||
def execute(
|
||||
self,
|
||||
func: Callable[[Unpack[_Ts]], object],
|
||||
args: tuple[Unpack[_Ts]],
|
||||
msg: object = None,
|
||||
level: int = 1,
|
||||
) -> None:
|
||||
def execute(self, func, args, msg=None, level=1):
|
||||
util.execute(func, args, msg, dry_run=self.dry_run)
|
||||
|
||||
def mkpath(self, name: str, mode: int = 0o777) -> None:
|
||||
def mkpath(self, name, mode=0o777):
|
||||
dir_util.mkpath(name, mode, dry_run=self.dry_run)
|
||||
|
||||
@overload
|
||||
def copy_file(
|
||||
self,
|
||||
infile: str | os.PathLike[str],
|
||||
outfile: _StrPathT,
|
||||
preserve_mode: bool = True,
|
||||
preserve_times: bool = True,
|
||||
link: str | None = None,
|
||||
level: int = 1,
|
||||
) -> tuple[_StrPathT | str, bool]: ...
|
||||
@overload
|
||||
def copy_file(
|
||||
self,
|
||||
infile: bytes | os.PathLike[bytes],
|
||||
outfile: _BytesPathT,
|
||||
preserve_mode: bool = True,
|
||||
preserve_times: bool = True,
|
||||
link: str | None = None,
|
||||
level: int = 1,
|
||||
) -> tuple[_BytesPathT | bytes, bool]: ...
|
||||
def copy_file(
|
||||
self,
|
||||
infile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
preserve_mode: bool = True,
|
||||
preserve_times: bool = True,
|
||||
link: str | None = None,
|
||||
level: int = 1,
|
||||
) -> tuple[str | os.PathLike[str] | bytes | os.PathLike[bytes], bool]:
|
||||
self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1
|
||||
):
|
||||
"""Copy a file respecting verbose, dry-run and force flags. (The
|
||||
former two default to whatever is in the Distribution object, and
|
||||
the latter defaults to false for commands that don't define it.)"""
|
||||
@@ -430,13 +360,13 @@ class Command:
|
||||
|
||||
def copy_tree(
|
||||
self,
|
||||
infile: str | os.PathLike[str],
|
||||
outfile: str,
|
||||
preserve_mode: bool = True,
|
||||
preserve_times: bool = True,
|
||||
preserve_symlinks: bool = False,
|
||||
level: int = 1,
|
||||
) -> list[str]:
|
||||
infile,
|
||||
outfile,
|
||||
preserve_mode=1,
|
||||
preserve_times=1,
|
||||
preserve_symlinks=0,
|
||||
level=1,
|
||||
):
|
||||
"""Copy an entire directory tree respecting verbose, dry-run,
|
||||
and force flags.
|
||||
"""
|
||||
@@ -450,60 +380,19 @@ class Command:
|
||||
dry_run=self.dry_run,
|
||||
)
|
||||
|
||||
@overload
|
||||
def move_file(
|
||||
self, src: str | os.PathLike[str], dst: _StrPathT, level: int = 1
|
||||
) -> _StrPathT | str: ...
|
||||
@overload
|
||||
def move_file(
|
||||
self, src: bytes | os.PathLike[bytes], dst: _BytesPathT, level: int = 1
|
||||
) -> _BytesPathT | bytes: ...
|
||||
def move_file(
|
||||
self,
|
||||
src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
level: int = 1,
|
||||
) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
|
||||
def move_file(self, src, dst, level=1):
|
||||
"""Move a file respecting dry-run flag."""
|
||||
return file_util.move_file(src, dst, dry_run=self.dry_run)
|
||||
|
||||
def spawn(
|
||||
self, cmd: MutableSequence[str], search_path: bool = True, level: int = 1
|
||||
) -> None:
|
||||
def spawn(self, cmd, search_path=1, level=1):
|
||||
"""Spawn an external command respecting dry-run flag."""
|
||||
from distutils.spawn import spawn
|
||||
|
||||
spawn(cmd, search_path, dry_run=self.dry_run)
|
||||
|
||||
@overload
|
||||
def make_archive(
|
||||
self,
|
||||
base_name: str,
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
|
||||
base_dir: str | None = None,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str: ...
|
||||
@overload
|
||||
def make_archive(
|
||||
self,
|
||||
base_name: str | os.PathLike[str],
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
base_dir: str | None = None,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str: ...
|
||||
def make_archive(
|
||||
self,
|
||||
base_name: str | os.PathLike[str],
|
||||
format: str,
|
||||
root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
|
||||
base_dir: str | None = None,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> str:
|
||||
self, base_name, format, root_dir=None, base_dir=None, owner=None, group=None
|
||||
):
|
||||
return archive_util.make_archive(
|
||||
base_name,
|
||||
format,
|
||||
@@ -515,15 +404,8 @@ class Command:
|
||||
)
|
||||
|
||||
def make_file(
|
||||
self,
|
||||
infiles: str | list[str] | tuple[str, ...],
|
||||
outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
|
||||
func: Callable[[Unpack[_Ts]], object],
|
||||
args: tuple[Unpack[_Ts]],
|
||||
exec_msg: object = None,
|
||||
skip_msg: object = None,
|
||||
level: int = 1,
|
||||
) -> None:
|
||||
self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1
|
||||
):
|
||||
"""Special case of 'execute()' for operations that process one or
|
||||
more input files and generate one output file. Works just like
|
||||
'execute()', except the operation is skipped and a different
|
||||
@@ -533,7 +415,7 @@ class Command:
|
||||
timestamp checks.
|
||||
"""
|
||||
if skip_msg is None:
|
||||
skip_msg = f"skipping {outfile} (inputs unchanged)"
|
||||
skip_msg = "skipping %s (inputs unchanged)" % outfile
|
||||
|
||||
# Allow 'infiles' to be a single string
|
||||
if isinstance(infiles, str):
|
||||
@@ -547,7 +429,7 @@ class Command:
|
||||
# If 'outfile' must be regenerated (either because it doesn't
|
||||
# exist, is out-of-date, or the 'force' flag is true) then
|
||||
# perform the action that presumably regenerates it
|
||||
if self.force or _modified.newer_group(infiles, outfile):
|
||||
if self.force or dep_util.newer_group(infiles, outfile):
|
||||
self.execute(func, args, exec_msg, level)
|
||||
# Otherwise, print the "skip" message
|
||||
else:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Package containing implementation of all the standard Distutils
|
||||
commands."""
|
||||
|
||||
__all__ = [
|
||||
__all__ = [ # noqa: F822
|
||||
'build',
|
||||
'build_py',
|
||||
'build_ext',
|
||||
@@ -16,8 +16,10 @@ __all__ = [
|
||||
'install_scripts',
|
||||
'install_data',
|
||||
'sdist',
|
||||
'register',
|
||||
'bdist',
|
||||
'bdist_dumb',
|
||||
'bdist_rpm',
|
||||
'check',
|
||||
'upload',
|
||||
]
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+6
-5
@@ -2,14 +2,15 @@
|
||||
Backward compatibility for homebrew builds on macOS.
|
||||
"""
|
||||
|
||||
import functools
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import sys
|
||||
import os
|
||||
import functools
|
||||
import subprocess
|
||||
import sysconfig
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
@functools.lru_cache()
|
||||
def enabled():
|
||||
"""
|
||||
Only enabled for Python 3.9 framework homebrew builds
|
||||
@@ -37,7 +38,7 @@ schemes = dict(
|
||||
)
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
@functools.lru_cache()
|
||||
def vars():
|
||||
if not enabled():
|
||||
return {}
|
||||
|
||||
@@ -3,49 +3,37 @@
|
||||
Implements the Distutils 'bdist' command (create a built [binary]
|
||||
distribution)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import warnings
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, ClassVar
|
||||
|
||||
from ..core import Command
|
||||
from ..errors import DistutilsOptionError, DistutilsPlatformError
|
||||
from ..util import get_platform
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import deprecated
|
||||
else:
|
||||
|
||||
def deprecated(message):
|
||||
return lambda fn: fn
|
||||
from distutils.core import Command
|
||||
from distutils.errors import DistutilsPlatformError, DistutilsOptionError
|
||||
from distutils.util import get_platform
|
||||
|
||||
|
||||
def show_formats():
|
||||
"""Print list of available formats (arguments to "--format" option)."""
|
||||
from ..fancy_getopt import FancyGetopt
|
||||
from distutils.fancy_getopt import FancyGetopt
|
||||
|
||||
formats = [
|
||||
("formats=" + format, None, bdist.format_commands[format][1])
|
||||
for format in bdist.format_commands
|
||||
]
|
||||
formats = []
|
||||
for format in bdist.format_commands:
|
||||
formats.append(("formats=" + format, None, bdist.format_commands[format][1]))
|
||||
pretty_printer = FancyGetopt(formats)
|
||||
pretty_printer.print_help("List of available distribution formats:")
|
||||
|
||||
|
||||
class ListCompat(dict[str, tuple[str, str]]):
|
||||
class ListCompat(dict):
|
||||
# adapter to allow for Setuptools compatibility in format_commands
|
||||
@deprecated("format_commands is now a dict. append is deprecated.")
|
||||
def append(self, item: object) -> None:
|
||||
def append(self, item):
|
||||
warnings.warn(
|
||||
"format_commands is now a dict. append is deprecated.",
|
||||
"""format_commands is now a dict. append is deprecated.""",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
class bdist(Command):
|
||||
|
||||
description = "create a built (binary) distribution"
|
||||
|
||||
user_options = [
|
||||
@@ -54,50 +42,52 @@ class bdist(Command):
|
||||
'plat-name=',
|
||||
'p',
|
||||
"platform name to embed in generated filenames "
|
||||
f"[default: {get_platform()}]",
|
||||
"(default: %s)" % get_platform(),
|
||||
),
|
||||
('formats=', None, "formats for distribution (comma-separated list)"),
|
||||
(
|
||||
'dist-dir=',
|
||||
'd',
|
||||
"directory to put final built distributions in [default: dist]",
|
||||
"directory to put final built distributions in " "[default: dist]",
|
||||
),
|
||||
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
|
||||
(
|
||||
'owner=',
|
||||
'u',
|
||||
"Owner name used when creating a tar file [default: current user]",
|
||||
"Owner name used when creating a tar file" " [default: current user]",
|
||||
),
|
||||
(
|
||||
'group=',
|
||||
'g',
|
||||
"Group name used when creating a tar file [default: current group]",
|
||||
"Group name used when creating a tar file" " [default: current group]",
|
||||
),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = ['skip-build']
|
||||
boolean_options = ['skip-build']
|
||||
|
||||
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
|
||||
help_options = [
|
||||
('help-formats', None, "lists available distribution formats", show_formats),
|
||||
]
|
||||
|
||||
# The following commands do not take a format option from bdist
|
||||
no_format_option: ClassVar[tuple[str, ...]] = ('bdist_rpm',)
|
||||
no_format_option = ('bdist_rpm',)
|
||||
|
||||
# This won't do in reality: will need to distinguish RPM-ish Linux,
|
||||
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
|
||||
default_format: ClassVar[dict[str, str]] = {'posix': 'gztar', 'nt': 'zip'}
|
||||
default_format = {'posix': 'gztar', 'nt': 'zip'}
|
||||
|
||||
# Define commands in preferred order for the --help-formats option
|
||||
format_commands = ListCompat({
|
||||
'rpm': ('bdist_rpm', "RPM distribution"),
|
||||
'gztar': ('bdist_dumb', "gzip'ed tar file"),
|
||||
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
|
||||
'xztar': ('bdist_dumb', "xz'ed tar file"),
|
||||
'ztar': ('bdist_dumb', "compressed tar file"),
|
||||
'tar': ('bdist_dumb', "tar file"),
|
||||
'zip': ('bdist_dumb', "ZIP file"),
|
||||
})
|
||||
format_commands = ListCompat(
|
||||
{
|
||||
'rpm': ('bdist_rpm', "RPM distribution"),
|
||||
'gztar': ('bdist_dumb', "gzip'ed tar file"),
|
||||
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
|
||||
'xztar': ('bdist_dumb', "xz'ed tar file"),
|
||||
'ztar': ('bdist_dumb', "compressed tar file"),
|
||||
'tar': ('bdist_dumb', "tar file"),
|
||||
'zip': ('bdist_dumb', "ZIP file"),
|
||||
}
|
||||
)
|
||||
|
||||
# for compatibility until consumers only reference format_commands
|
||||
format_command = format_commands
|
||||
@@ -107,11 +97,11 @@ class bdist(Command):
|
||||
self.plat_name = None
|
||||
self.formats = None
|
||||
self.dist_dir = None
|
||||
self.skip_build = False
|
||||
self.skip_build = 0
|
||||
self.group = None
|
||||
self.owner = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
def finalize_options(self):
|
||||
# have to finalize 'plat_name' before 'bdist_base'
|
||||
if self.plat_name is None:
|
||||
if self.skip_build:
|
||||
@@ -133,20 +123,20 @@ class bdist(Command):
|
||||
except KeyError:
|
||||
raise DistutilsPlatformError(
|
||||
"don't know how to create built distributions "
|
||||
f"on platform {os.name}"
|
||||
"on platform %s" % os.name
|
||||
)
|
||||
|
||||
if self.dist_dir is None:
|
||||
self.dist_dir = "dist"
|
||||
|
||||
def run(self) -> None:
|
||||
def run(self):
|
||||
# Figure out which sub-commands we need to run.
|
||||
commands = []
|
||||
for format in self.formats:
|
||||
try:
|
||||
commands.append(self.format_commands[format][0])
|
||||
except KeyError:
|
||||
raise DistutilsOptionError(f"invalid format '{format}'")
|
||||
raise DistutilsOptionError("invalid format '%s'" % format)
|
||||
|
||||
# Reinitialize and run each command.
|
||||
for i in range(len(self.formats)):
|
||||
@@ -163,5 +153,5 @@ class bdist(Command):
|
||||
# If we're going to need to run this command again, tell it to
|
||||
# keep its temporary files around so subsequent runs go faster.
|
||||
if cmd_name in commands[i + 1 :]:
|
||||
sub_cmd.keep_temp = True
|
||||
sub_cmd.keep_temp = 1
|
||||
self.run_command(cmd_name)
|
||||
|
||||
+25
-22
@@ -5,17 +5,16 @@ distribution -- i.e., just an archive to be unpacked under $prefix or
|
||||
$exec_prefix)."""
|
||||
|
||||
import os
|
||||
from distutils._log import log
|
||||
from typing import ClassVar
|
||||
|
||||
from ..core import Command
|
||||
from ..dir_util import ensure_relative, remove_tree
|
||||
from ..errors import DistutilsPlatformError
|
||||
from ..sysconfig import get_python_version
|
||||
from ..util import get_platform
|
||||
from distutils.core import Command
|
||||
from distutils.util import get_platform
|
||||
from distutils.dir_util import remove_tree, ensure_relative
|
||||
from distutils.errors import DistutilsPlatformError
|
||||
from distutils.sysconfig import get_python_version
|
||||
from distutils import log
|
||||
|
||||
|
||||
class bdist_dumb(Command):
|
||||
|
||||
description = "create a \"dumb\" built distribution"
|
||||
|
||||
user_options = [
|
||||
@@ -24,38 +23,39 @@ class bdist_dumb(Command):
|
||||
'plat-name=',
|
||||
'p',
|
||||
"platform name to embed in generated filenames "
|
||||
f"[default: {get_platform()}]",
|
||||
"(default: %s)" % get_platform(),
|
||||
),
|
||||
(
|
||||
'format=',
|
||||
'f',
|
||||
"archive format to create (tar, gztar, bztar, xztar, ztar, zip)",
|
||||
"archive format to create (tar, gztar, bztar, xztar, " "ztar, zip)",
|
||||
),
|
||||
(
|
||||
'keep-temp',
|
||||
'k',
|
||||
"keep the pseudo-installation tree around after creating the distribution archive",
|
||||
"keep the pseudo-installation tree around after "
|
||||
+ "creating the distribution archive",
|
||||
),
|
||||
('dist-dir=', 'd', "directory to put final built distributions in"),
|
||||
('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
|
||||
(
|
||||
'relative',
|
||||
None,
|
||||
"build the archive using relative paths [default: false]",
|
||||
"build the archive using relative paths " "(default: false)",
|
||||
),
|
||||
(
|
||||
'owner=',
|
||||
'u',
|
||||
"Owner name used when creating a tar file [default: current user]",
|
||||
"Owner name used when creating a tar file" " [default: current user]",
|
||||
),
|
||||
(
|
||||
'group=',
|
||||
'g',
|
||||
"Group name used when creating a tar file [default: current group]",
|
||||
"Group name used when creating a tar file" " [default: current group]",
|
||||
),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = ['keep-temp', 'skip-build', 'relative']
|
||||
boolean_options = ['keep-temp', 'skip-build', 'relative']
|
||||
|
||||
default_format = {'posix': 'gztar', 'nt': 'zip'}
|
||||
|
||||
@@ -63,10 +63,10 @@ class bdist_dumb(Command):
|
||||
self.bdist_dir = None
|
||||
self.plat_name = None
|
||||
self.format = None
|
||||
self.keep_temp = False
|
||||
self.keep_temp = 0
|
||||
self.dist_dir = None
|
||||
self.skip_build = None
|
||||
self.relative = False
|
||||
self.relative = 0
|
||||
self.owner = None
|
||||
self.group = None
|
||||
|
||||
@@ -81,7 +81,7 @@ class bdist_dumb(Command):
|
||||
except KeyError:
|
||||
raise DistutilsPlatformError(
|
||||
"don't know how to create dumb built distributions "
|
||||
f"on platform {os.name}"
|
||||
"on platform %s" % os.name
|
||||
)
|
||||
|
||||
self.set_undefined_options(
|
||||
@@ -95,17 +95,19 @@ class bdist_dumb(Command):
|
||||
if not self.skip_build:
|
||||
self.run_command('build')
|
||||
|
||||
install = self.reinitialize_command('install', reinit_subcommands=True)
|
||||
install = self.reinitialize_command('install', reinit_subcommands=1)
|
||||
install.root = self.bdist_dir
|
||||
install.skip_build = self.skip_build
|
||||
install.warn_dir = False
|
||||
install.warn_dir = 0
|
||||
|
||||
log.info("installing to %s", self.bdist_dir)
|
||||
self.run_command('install')
|
||||
|
||||
# And make an archive relative to the root of the
|
||||
# pseudo-installation tree.
|
||||
archive_basename = f"{self.distribution.get_fullname()}.{self.plat_name}"
|
||||
archive_basename = "{}.{}".format(
|
||||
self.distribution.get_fullname(), self.plat_name
|
||||
)
|
||||
|
||||
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
|
||||
if not self.relative:
|
||||
@@ -116,7 +118,8 @@ class bdist_dumb(Command):
|
||||
):
|
||||
raise DistutilsPlatformError(
|
||||
"can't make a dumb built distribution where "
|
||||
f"base and platbase are different ({install.install_base!r}, {install.install_platbase!r})"
|
||||
"base and platbase are different (%s, %s)"
|
||||
% (repr(install.install_base), repr(install.install_platbase))
|
||||
)
|
||||
else:
|
||||
archive_root = os.path.join(
|
||||
|
||||
+94
-77
@@ -3,25 +3,25 @@
|
||||
Implements the Distutils 'bdist_rpm' command (create RPM source and binary
|
||||
distributions)."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from distutils._log import log
|
||||
from typing import ClassVar
|
||||
import os
|
||||
|
||||
from ..core import Command
|
||||
from ..debug import DEBUG
|
||||
from ..errors import (
|
||||
DistutilsExecError,
|
||||
DistutilsFileError,
|
||||
from distutils.core import Command
|
||||
from distutils.debug import DEBUG
|
||||
from distutils.file_util import write_file
|
||||
from distutils.errors import (
|
||||
DistutilsOptionError,
|
||||
DistutilsPlatformError,
|
||||
DistutilsFileError,
|
||||
DistutilsExecError,
|
||||
)
|
||||
from ..file_util import write_file
|
||||
from ..sysconfig import get_python_version
|
||||
from distutils.sysconfig import get_python_version
|
||||
from distutils import log
|
||||
|
||||
|
||||
class bdist_rpm(Command):
|
||||
|
||||
description = "create an RPM distribution"
|
||||
|
||||
user_options = [
|
||||
@@ -35,13 +35,13 @@ class bdist_rpm(Command):
|
||||
(
|
||||
'dist-dir=',
|
||||
'd',
|
||||
"directory to put final RPM files in (and .spec files if --spec-only)",
|
||||
"directory to put final RPM files in " "(and .spec files if --spec-only)",
|
||||
),
|
||||
(
|
||||
'python=',
|
||||
None,
|
||||
"path to Python interpreter to hard-code in the .spec file "
|
||||
"[default: \"python\"]",
|
||||
"(default: \"python\")",
|
||||
),
|
||||
(
|
||||
'fix-python',
|
||||
@@ -76,7 +76,7 @@ class bdist_rpm(Command):
|
||||
(
|
||||
'packager=',
|
||||
None,
|
||||
"RPM packager (eg. \"Jane Doe <jane@example.net>\") [default: vendor]",
|
||||
"RPM packager (eg. \"Jane Doe <jane@example.net>\") " "[default: vendor]",
|
||||
),
|
||||
('doc-files=', None, "list of documentation files (space or comma-separated)"),
|
||||
('changelog=', None, "RPM changelog"),
|
||||
@@ -137,7 +137,7 @@ class bdist_rpm(Command):
|
||||
('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = [
|
||||
boolean_options = [
|
||||
'keep-temp',
|
||||
'use-rpm-opt-flags',
|
||||
'rpm3-mode',
|
||||
@@ -145,7 +145,7 @@ class bdist_rpm(Command):
|
||||
'quiet',
|
||||
]
|
||||
|
||||
negative_opt: ClassVar[dict[str, str]] = {
|
||||
negative_opt = {
|
||||
'no-keep-temp': 'keep-temp',
|
||||
'no-rpm-opt-flags': 'use-rpm-opt-flags',
|
||||
'rpm2-mode': 'rpm3-mode',
|
||||
@@ -188,15 +188,15 @@ class bdist_rpm(Command):
|
||||
self.build_requires = None
|
||||
self.obsoletes = None
|
||||
|
||||
self.keep_temp = False
|
||||
self.use_rpm_opt_flags = True
|
||||
self.rpm3_mode = True
|
||||
self.no_autoreq = False
|
||||
self.keep_temp = 0
|
||||
self.use_rpm_opt_flags = 1
|
||||
self.rpm3_mode = 1
|
||||
self.no_autoreq = 0
|
||||
|
||||
self.force_arch = None
|
||||
self.quiet = False
|
||||
self.quiet = 0
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
def finalize_options(self):
|
||||
self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
|
||||
if self.rpm_base is None:
|
||||
if not self.rpm3_mode:
|
||||
@@ -215,7 +215,7 @@ class bdist_rpm(Command):
|
||||
|
||||
if os.name != 'posix':
|
||||
raise DistutilsPlatformError(
|
||||
f"don't know how to create RPM distributions on platform {os.name}"
|
||||
"don't know how to create RPM " "distributions on platform %s" % os.name
|
||||
)
|
||||
if self.binary_only and self.source_only:
|
||||
raise DistutilsOptionError(
|
||||
@@ -224,16 +224,17 @@ class bdist_rpm(Command):
|
||||
|
||||
# don't pass CFLAGS to pure python distributions
|
||||
if not self.distribution.has_ext_modules():
|
||||
self.use_rpm_opt_flags = False
|
||||
self.use_rpm_opt_flags = 0
|
||||
|
||||
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
|
||||
self.finalize_package_data()
|
||||
|
||||
def finalize_package_data(self) -> None:
|
||||
def finalize_package_data(self):
|
||||
self.ensure_string('group', "Development/Libraries")
|
||||
self.ensure_string(
|
||||
'vendor',
|
||||
f"{self.distribution.get_contact()} <{self.distribution.get_contact_email()}>",
|
||||
"%s <%s>"
|
||||
% (self.distribution.get_contact(), self.distribution.get_contact_email()),
|
||||
)
|
||||
self.ensure_string('packager')
|
||||
self.ensure_string_list('doc_files')
|
||||
@@ -275,7 +276,7 @@ class bdist_rpm(Command):
|
||||
|
||||
self.ensure_string('force_arch')
|
||||
|
||||
def run(self) -> None: # noqa: C901
|
||||
def run(self): # noqa: C901
|
||||
if DEBUG:
|
||||
print("before _get_package_data():")
|
||||
print("vendor =", self.vendor)
|
||||
@@ -296,9 +297,9 @@ class bdist_rpm(Command):
|
||||
|
||||
# Spec file goes into 'dist_dir' if '--spec-only specified',
|
||||
# build/rpm.<plat> otherwise.
|
||||
spec_path = os.path.join(spec_dir, f"{self.distribution.get_name()}.spec")
|
||||
spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name())
|
||||
self.execute(
|
||||
write_file, (spec_path, self._make_spec_file()), f"writing '{spec_path}'"
|
||||
write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path
|
||||
)
|
||||
|
||||
if self.spec_only: # stop if requested
|
||||
@@ -323,7 +324,7 @@ class bdist_rpm(Command):
|
||||
if os.path.exists(self.icon):
|
||||
self.copy_file(self.icon, source_dir)
|
||||
else:
|
||||
raise DistutilsFileError(f"icon file '{self.icon}' does not exist")
|
||||
raise DistutilsFileError("icon file '%s' does not exist" % self.icon)
|
||||
|
||||
# build package
|
||||
log.info("building RPMs")
|
||||
@@ -335,9 +336,9 @@ class bdist_rpm(Command):
|
||||
rpm_cmd.append('-bb')
|
||||
else:
|
||||
rpm_cmd.append('-ba')
|
||||
rpm_cmd.extend(['--define', f'__python {self.python}'])
|
||||
rpm_cmd.extend(['--define', '__python %s' % self.python])
|
||||
if self.rpm3_mode:
|
||||
rpm_cmd.extend(['--define', f'_topdir {os.path.abspath(self.rpm_base)}'])
|
||||
rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)])
|
||||
if not self.keep_temp:
|
||||
rpm_cmd.append('--clean')
|
||||
|
||||
@@ -352,7 +353,11 @@ class bdist_rpm(Command):
|
||||
nvr_string = "%{name}-%{version}-%{release}"
|
||||
src_rpm = nvr_string + ".src.rpm"
|
||||
non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
|
||||
q_cmd = rf"rpm -q --qf '{src_rpm} {non_src_rpm}\n' --specfile '{spec_path}'"
|
||||
q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format(
|
||||
src_rpm,
|
||||
non_src_rpm,
|
||||
spec_path,
|
||||
)
|
||||
|
||||
out = os.popen(q_cmd)
|
||||
try:
|
||||
@@ -371,7 +376,7 @@ class bdist_rpm(Command):
|
||||
|
||||
status = out.close()
|
||||
if status:
|
||||
raise DistutilsExecError(f"Failed to execute: {q_cmd!r}")
|
||||
raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd))
|
||||
|
||||
finally:
|
||||
out.close()
|
||||
@@ -397,11 +402,9 @@ class bdist_rpm(Command):
|
||||
if os.path.exists(rpm):
|
||||
self.move_file(rpm, self.dist_dir)
|
||||
filename = os.path.join(self.dist_dir, os.path.basename(rpm))
|
||||
self.distribution.dist_files.append((
|
||||
'bdist_rpm',
|
||||
pyversion,
|
||||
filename,
|
||||
))
|
||||
self.distribution.dist_files.append(
|
||||
('bdist_rpm', pyversion, filename)
|
||||
)
|
||||
|
||||
def _dist_path(self, path):
|
||||
return os.path.join(self.dist_dir, os.path.basename(path))
|
||||
@@ -426,14 +429,14 @@ class bdist_rpm(Command):
|
||||
# Generate a potential replacement value for __os_install_post (whilst
|
||||
# normalizing the whitespace to simplify the test for whether the
|
||||
# invocation of brp-python-bytecompile passes in __python):
|
||||
vendor_hook = '\n'.join([
|
||||
f' {line.strip()} \\' for line in vendor_hook.splitlines()
|
||||
])
|
||||
vendor_hook = '\n'.join(
|
||||
[' %s \\' % line.strip() for line in vendor_hook.splitlines()]
|
||||
)
|
||||
problem = "brp-python-bytecompile \\\n"
|
||||
fixed = "brp-python-bytecompile %{__python} \\\n"
|
||||
fixed_hook = vendor_hook.replace(problem, fixed)
|
||||
if fixed_hook != vendor_hook:
|
||||
spec_file.append('# Workaround for https://bugs.python.org/issue14443')
|
||||
spec_file.append('# Workaround for http://bugs.python.org/issue14443')
|
||||
spec_file.append('%define __os_install_post ' + fixed_hook + '\n')
|
||||
|
||||
# put locale summaries into spec file
|
||||
@@ -443,11 +446,13 @@ class bdist_rpm(Command):
|
||||
# spec_file.append('Summary(%s): %s' % (locale,
|
||||
# self.summaries[locale]))
|
||||
|
||||
spec_file.extend([
|
||||
'Name: %{name}',
|
||||
'Version: %{version}',
|
||||
'Release: %{release}',
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'Name: %{name}',
|
||||
'Version: %{version}',
|
||||
'Release: %{release}',
|
||||
]
|
||||
)
|
||||
|
||||
# XXX yuck! this filename is available from the "sdist" command,
|
||||
# but only after it has run: and we create the spec file before
|
||||
@@ -457,19 +462,21 @@ class bdist_rpm(Command):
|
||||
else:
|
||||
spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz')
|
||||
|
||||
spec_file.extend([
|
||||
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
|
||||
'Group: ' + self.group,
|
||||
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
|
||||
'Prefix: %{_prefix}',
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
|
||||
'Group: ' + self.group,
|
||||
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
|
||||
'Prefix: %{_prefix}',
|
||||
]
|
||||
)
|
||||
|
||||
if not self.force_arch:
|
||||
# noarch if no extension modules
|
||||
if not self.distribution.has_ext_modules():
|
||||
spec_file.append('BuildArch: noarch')
|
||||
else:
|
||||
spec_file.append(f'BuildArch: {self.force_arch}')
|
||||
spec_file.append('BuildArch: %s' % self.force_arch)
|
||||
|
||||
for field in (
|
||||
'Vendor',
|
||||
@@ -483,7 +490,7 @@ class bdist_rpm(Command):
|
||||
if isinstance(val, list):
|
||||
spec_file.append('{}: {}'.format(field, ' '.join(val)))
|
||||
elif val is not None:
|
||||
spec_file.append(f'{field}: {val}')
|
||||
spec_file.append('{}: {}'.format(field, val))
|
||||
|
||||
if self.distribution.get_url():
|
||||
spec_file.append('Url: ' + self.distribution.get_url())
|
||||
@@ -500,11 +507,13 @@ class bdist_rpm(Command):
|
||||
if self.no_autoreq:
|
||||
spec_file.append('AutoReq: 0')
|
||||
|
||||
spec_file.extend([
|
||||
'',
|
||||
'%description',
|
||||
self.distribution.get_long_description() or "",
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'',
|
||||
'%description',
|
||||
self.distribution.get_long_description() or "",
|
||||
]
|
||||
)
|
||||
|
||||
# put locale descriptions into spec file
|
||||
# XXX again, suppressed because config file syntax doesn't
|
||||
@@ -518,8 +527,8 @@ class bdist_rpm(Command):
|
||||
|
||||
# rpm scripts
|
||||
# figure out default build script
|
||||
def_setup_call = f"{self.python} {os.path.basename(sys.argv[0])}"
|
||||
def_build = f"{def_setup_call} build"
|
||||
def_setup_call = "{} {}".format(self.python, os.path.basename(sys.argv[0]))
|
||||
def_build = "%s build" % def_setup_call
|
||||
if self.use_rpm_opt_flags:
|
||||
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
|
||||
|
||||
@@ -529,7 +538,9 @@ class bdist_rpm(Command):
|
||||
# that we open and interpolate into the spec file, but the defaults
|
||||
# are just text that we drop in as-is. Hmmm.
|
||||
|
||||
install_cmd = f'{def_setup_call} install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES'
|
||||
install_cmd = (
|
||||
'%s install -O1 --root=$RPM_BUILD_ROOT ' '--record=INSTALLED_FILES'
|
||||
) % def_setup_call
|
||||
|
||||
script_options = [
|
||||
('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"),
|
||||
@@ -543,15 +554,17 @@ class bdist_rpm(Command):
|
||||
('postun', 'post_uninstall', None),
|
||||
]
|
||||
|
||||
for rpm_opt, attr, default in script_options:
|
||||
for (rpm_opt, attr, default) in script_options:
|
||||
# Insert contents of file referred to, if no file is referred to
|
||||
# use 'default' as contents of script
|
||||
val = getattr(self, attr)
|
||||
if val or default:
|
||||
spec_file.extend([
|
||||
'',
|
||||
'%' + rpm_opt,
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'',
|
||||
'%' + rpm_opt,
|
||||
]
|
||||
)
|
||||
if val:
|
||||
with open(val) as f:
|
||||
spec_file.extend(f.read().split('\n'))
|
||||
@@ -559,20 +572,24 @@ class bdist_rpm(Command):
|
||||
spec_file.append(default)
|
||||
|
||||
# files section
|
||||
spec_file.extend([
|
||||
'',
|
||||
'%files -f INSTALLED_FILES',
|
||||
'%defattr(-,root,root)',
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'',
|
||||
'%files -f INSTALLED_FILES',
|
||||
'%defattr(-,root,root)',
|
||||
]
|
||||
)
|
||||
|
||||
if self.doc_files:
|
||||
spec_file.append('%doc ' + ' '.join(self.doc_files))
|
||||
|
||||
if self.changelog:
|
||||
spec_file.extend([
|
||||
'',
|
||||
'%changelog',
|
||||
])
|
||||
spec_file.extend(
|
||||
[
|
||||
'',
|
||||
'%changelog',
|
||||
]
|
||||
)
|
||||
spec_file.extend(self.changelog)
|
||||
|
||||
return spec_file
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
|
||||
Implements the Distutils 'build' command."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sysconfig
|
||||
from collections.abc import Callable
|
||||
from typing import ClassVar
|
||||
import os
|
||||
from distutils.core import Command
|
||||
from distutils.errors import DistutilsOptionError
|
||||
from distutils.util import get_platform
|
||||
|
||||
from ..ccompiler import show_compilers
|
||||
from ..core import Command
|
||||
from ..errors import DistutilsOptionError
|
||||
from ..util import get_platform
|
||||
|
||||
def show_compilers():
|
||||
from distutils.ccompiler import show_compilers
|
||||
|
||||
show_compilers()
|
||||
|
||||
|
||||
class build(Command):
|
||||
|
||||
description = "build everything needed to install"
|
||||
|
||||
user_options = [
|
||||
@@ -26,14 +26,16 @@ class build(Command):
|
||||
(
|
||||
'build-lib=',
|
||||
None,
|
||||
"build directory for all distribution (defaults to either build-purelib or build-platlib",
|
||||
"build directory for all distribution (defaults to either "
|
||||
+ "build-purelib or build-platlib",
|
||||
),
|
||||
('build-scripts=', None, "build directory for scripts"),
|
||||
('build-temp=', 't', "temporary build directory"),
|
||||
(
|
||||
'plat-name=',
|
||||
'p',
|
||||
f"platform name to build for, if supported [default: {get_platform()}]",
|
||||
"platform name to build for, if supported "
|
||||
"(default: %s)" % get_platform(),
|
||||
),
|
||||
('compiler=', 'c', "specify the compiler type"),
|
||||
('parallel=', 'j', "number of parallel build jobs"),
|
||||
@@ -42,9 +44,9 @@ class build(Command):
|
||||
('executable=', 'e', "specify final destination interpreter path (build.py)"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = ['debug', 'force']
|
||||
boolean_options = ['debug', 'force']
|
||||
|
||||
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
|
||||
help_options = [
|
||||
('help-compiler', None, "list available compilers", show_compilers),
|
||||
]
|
||||
|
||||
@@ -60,11 +62,11 @@ class build(Command):
|
||||
self.compiler = None
|
||||
self.plat_name = None
|
||||
self.debug = None
|
||||
self.force = False
|
||||
self.force = 0
|
||||
self.executable = None
|
||||
self.parallel = None
|
||||
|
||||
def finalize_options(self) -> None: # noqa: C901
|
||||
def finalize_options(self): # noqa: C901
|
||||
if self.plat_name is None:
|
||||
self.plat_name = get_platform()
|
||||
else:
|
||||
@@ -77,11 +79,7 @@ class build(Command):
|
||||
"using './configure --help' on your platform)"
|
||||
)
|
||||
|
||||
plat_specifier = f".{self.plat_name}-{sys.implementation.cache_tag}"
|
||||
|
||||
# Python 3.13+ with --disable-gil shouldn't share build directories
|
||||
if sysconfig.get_config_var('Py_GIL_DISABLED'):
|
||||
plat_specifier += 't'
|
||||
plat_specifier = ".{}-{}".format(self.plat_name, sys.implementation.cache_tag)
|
||||
|
||||
# Make it so Python 2.x and Python 2.x with --with-pydebug don't
|
||||
# share the same build directories. Doing so confuses the build
|
||||
@@ -112,8 +110,7 @@ class build(Command):
|
||||
self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier)
|
||||
if self.build_scripts is None:
|
||||
self.build_scripts = os.path.join(
|
||||
self.build_base,
|
||||
f'scripts-{sys.version_info.major}.{sys.version_info.minor}',
|
||||
self.build_base, 'scripts-%d.%d' % sys.version_info[:2]
|
||||
)
|
||||
|
||||
if self.executable is None and sys.executable:
|
||||
@@ -125,7 +122,7 @@ class build(Command):
|
||||
except ValueError:
|
||||
raise DistutilsOptionError("parallel should be an integer")
|
||||
|
||||
def run(self) -> None:
|
||||
def run(self):
|
||||
# Run all relevant sub-commands. This will be some subset of:
|
||||
# - build_py - pure Python modules
|
||||
# - build_clib - standalone C libraries
|
||||
|
||||
+32
-25
@@ -4,6 +4,7 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library
|
||||
that is included in the module distribution and needed by an extension
|
||||
module."""
|
||||
|
||||
|
||||
# XXX this module has *lots* of code ripped-off quite transparently from
|
||||
# build_ext.py -- not surprisingly really, as the work required to build
|
||||
# a static library from a collection of C source files is not really all
|
||||
@@ -12,23 +13,25 @@ module."""
|
||||
# necessary refactoring to account for the overlap in code between the
|
||||
# two modules, mainly because a number of subtle details changed in the
|
||||
# cut 'n paste. Sigh.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from collections.abc import Callable
|
||||
from distutils._log import log
|
||||
from typing import ClassVar
|
||||
from distutils.core import Command
|
||||
from distutils.errors import DistutilsSetupError
|
||||
from distutils.sysconfig import customize_compiler
|
||||
from distutils import log
|
||||
|
||||
from ..ccompiler import new_compiler, show_compilers
|
||||
from ..core import Command
|
||||
from ..errors import DistutilsSetupError
|
||||
from ..sysconfig import customize_compiler
|
||||
|
||||
def show_compilers():
|
||||
from distutils.ccompiler import show_compilers
|
||||
|
||||
show_compilers()
|
||||
|
||||
|
||||
class build_clib(Command):
|
||||
|
||||
description = "build C/C++ libraries used by Python extensions"
|
||||
|
||||
user_options: ClassVar[list[tuple[str, str, str]]] = [
|
||||
user_options = [
|
||||
('build-clib=', 'b', "directory to build C/C++ libraries to"),
|
||||
('build-temp=', 't', "directory to put temporary build by-products"),
|
||||
('debug', 'g', "compile with debugging information"),
|
||||
@@ -36,9 +39,9 @@ class build_clib(Command):
|
||||
('compiler=', 'c', "specify the compiler type"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = ['debug', 'force']
|
||||
boolean_options = ['debug', 'force']
|
||||
|
||||
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
|
||||
help_options = [
|
||||
('help-compiler', None, "list available compilers", show_compilers),
|
||||
]
|
||||
|
||||
@@ -54,10 +57,10 @@ class build_clib(Command):
|
||||
self.define = None
|
||||
self.undef = None
|
||||
self.debug = None
|
||||
self.force = False
|
||||
self.force = 0
|
||||
self.compiler = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
def finalize_options(self):
|
||||
# This might be confusing: both build-clib and build-temp default
|
||||
# to build-temp as defined by the "build" command. This is because
|
||||
# I think that C libraries are really just temporary build
|
||||
@@ -84,10 +87,13 @@ class build_clib(Command):
|
||||
# XXX same as for build_ext -- what about 'self.define' and
|
||||
# 'self.undef' ?
|
||||
|
||||
def run(self) -> None:
|
||||
def run(self):
|
||||
if not self.libraries:
|
||||
return
|
||||
|
||||
# Yech -- this is cut 'n pasted from build_ext.py!
|
||||
from distutils.ccompiler import new_compiler
|
||||
|
||||
self.compiler = new_compiler(
|
||||
compiler=self.compiler, dry_run=self.dry_run, force=self.force
|
||||
)
|
||||
@@ -97,7 +103,7 @@ class build_clib(Command):
|
||||
self.compiler.set_include_dirs(self.include_dirs)
|
||||
if self.define is not None:
|
||||
# 'define' option is a list of (name,value) tuples
|
||||
for name, value in self.define:
|
||||
for (name, value) in self.define:
|
||||
self.compiler.define_macro(name, value)
|
||||
if self.undef is not None:
|
||||
for macro in self.undef:
|
||||
@@ -105,7 +111,7 @@ class build_clib(Command):
|
||||
|
||||
self.build_libraries(self.libraries)
|
||||
|
||||
def check_library_list(self, libraries) -> None:
|
||||
def check_library_list(self, libraries):
|
||||
"""Ensure that the list of libraries is valid.
|
||||
|
||||
`library` is presumably provided as a command option 'libraries'.
|
||||
@@ -132,7 +138,8 @@ class build_clib(Command):
|
||||
|
||||
if '/' in name or (os.sep != '/' and os.sep in name):
|
||||
raise DistutilsSetupError(
|
||||
f"bad library name '{lib[0]}': may not contain directory separators"
|
||||
"bad library name '%s': "
|
||||
"may not contain directory separators" % lib[0]
|
||||
)
|
||||
|
||||
if not isinstance(build_info, dict):
|
||||
@@ -148,33 +155,33 @@ class build_clib(Command):
|
||||
return None
|
||||
|
||||
lib_names = []
|
||||
for lib_name, _build_info in self.libraries:
|
||||
for (lib_name, build_info) in self.libraries:
|
||||
lib_names.append(lib_name)
|
||||
return lib_names
|
||||
|
||||
def get_source_files(self):
|
||||
self.check_library_list(self.libraries)
|
||||
filenames = []
|
||||
for lib_name, build_info in self.libraries:
|
||||
for (lib_name, build_info) in self.libraries:
|
||||
sources = build_info.get('sources')
|
||||
if sources is None or not isinstance(sources, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
f"in 'libraries' option (library '{lib_name}'), "
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'sources' must be present and must be "
|
||||
"a list of source filenames"
|
||||
"a list of source filenames" % lib_name
|
||||
)
|
||||
|
||||
filenames.extend(sources)
|
||||
return filenames
|
||||
|
||||
def build_libraries(self, libraries) -> None:
|
||||
for lib_name, build_info in libraries:
|
||||
def build_libraries(self, libraries):
|
||||
for (lib_name, build_info) in libraries:
|
||||
sources = build_info.get('sources')
|
||||
if sources is None or not isinstance(sources, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
f"in 'libraries' option (library '{lib_name}'), "
|
||||
"in 'libraries' option (library '%s'), "
|
||||
"'sources' must be present and must be "
|
||||
"a list of source filenames"
|
||||
"a list of source filenames" % lib_name
|
||||
)
|
||||
sources = list(sources)
|
||||
|
||||
|
||||
+78
-103
@@ -4,38 +4,42 @@ Implements the Distutils 'build_ext' command, for building extension
|
||||
modules (currently limited to C extensions, should accommodate C++
|
||||
extensions ASAP)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from distutils._log import log
|
||||
from site import USER_BASE
|
||||
from typing import ClassVar
|
||||
|
||||
from .._modified import newer_group
|
||||
from ..ccompiler import new_compiler, show_compilers
|
||||
from ..core import Command
|
||||
from ..errors import (
|
||||
CCompilerError,
|
||||
CompileError,
|
||||
DistutilsError,
|
||||
from distutils.core import Command
|
||||
from distutils.errors import (
|
||||
DistutilsOptionError,
|
||||
DistutilsPlatformError,
|
||||
DistutilsSetupError,
|
||||
CCompilerError,
|
||||
DistutilsError,
|
||||
CompileError,
|
||||
DistutilsPlatformError,
|
||||
)
|
||||
from ..extension import Extension
|
||||
from ..sysconfig import customize_compiler, get_config_h_filename, get_python_version
|
||||
from ..util import get_platform, is_freethreaded, is_mingw
|
||||
from distutils.sysconfig import customize_compiler, get_python_version
|
||||
from distutils.sysconfig import get_config_h_filename
|
||||
from distutils.dep_util import newer_group
|
||||
from distutils.extension import Extension
|
||||
from distutils.util import get_platform
|
||||
from distutils import log
|
||||
from . import py37compat
|
||||
|
||||
from site import USER_BASE
|
||||
|
||||
# An extension name is just a dot-separated list of Python NAMEs (ie.
|
||||
# the same as a fully-qualified module name).
|
||||
extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
|
||||
|
||||
|
||||
def show_compilers():
|
||||
from distutils.ccompiler import show_compilers
|
||||
|
||||
show_compilers()
|
||||
|
||||
|
||||
class build_ext(Command):
|
||||
|
||||
description = "build C/C++ extensions (compile/link to build directory)"
|
||||
|
||||
# XXX thoughts on how to deal with complex command-line options like
|
||||
@@ -56,7 +60,7 @@ class build_ext(Command):
|
||||
# takes care of both command-line and client options
|
||||
# in between initialize_options() and finalize_options())
|
||||
|
||||
sep_by = f" (separated by '{os.pathsep}')"
|
||||
sep_by = " (separated by '%s')" % os.pathsep
|
||||
user_options = [
|
||||
('build-lib=', 'b', "directory for compiled extension modules"),
|
||||
('build-temp=', 't', "directory for temporary files (build by-products)"),
|
||||
@@ -64,13 +68,13 @@ class build_ext(Command):
|
||||
'plat-name=',
|
||||
'p',
|
||||
"platform name to cross-compile for, if supported "
|
||||
f"[default: {get_platform()}]",
|
||||
"(default: %s)" % get_platform(),
|
||||
),
|
||||
(
|
||||
'inplace',
|
||||
'i',
|
||||
"ignore build-lib and put compiled extensions into the source "
|
||||
"directory alongside your pure Python modules",
|
||||
+ "directory alongside your pure Python modules",
|
||||
),
|
||||
(
|
||||
'include-dirs=',
|
||||
@@ -97,15 +101,9 @@ class build_ext(Command):
|
||||
('user', None, "add user include, library and rpath"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = [
|
||||
'inplace',
|
||||
'debug',
|
||||
'force',
|
||||
'swig-cpp',
|
||||
'user',
|
||||
]
|
||||
boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user']
|
||||
|
||||
help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
|
||||
help_options = [
|
||||
('help-compiler', None, "list available compilers", show_compilers),
|
||||
]
|
||||
|
||||
@@ -114,7 +112,7 @@ class build_ext(Command):
|
||||
self.build_lib = None
|
||||
self.plat_name = None
|
||||
self.build_temp = None
|
||||
self.inplace = False
|
||||
self.inplace = 0
|
||||
self.package = None
|
||||
|
||||
self.include_dirs = None
|
||||
@@ -133,32 +131,7 @@ class build_ext(Command):
|
||||
self.user = None
|
||||
self.parallel = None
|
||||
|
||||
@staticmethod
|
||||
def _python_lib_dir(sysconfig):
|
||||
"""
|
||||
Resolve Python's library directory for building extensions
|
||||
that rely on a shared Python library.
|
||||
|
||||
See python/cpython#44264 and python/cpython#48686
|
||||
"""
|
||||
if not sysconfig.get_config_var('Py_ENABLE_SHARED'):
|
||||
return
|
||||
|
||||
if sysconfig.python_build:
|
||||
yield '.'
|
||||
return
|
||||
|
||||
if sys.platform == 'zos':
|
||||
# On z/OS, a user is not required to install Python to
|
||||
# a predetermined path, but can use Python portably
|
||||
installed_dir = sysconfig.get_config_var('base')
|
||||
lib_dir = sysconfig.get_config_var('platlibdir')
|
||||
yield os.path.join(installed_dir, lib_dir)
|
||||
else:
|
||||
# building third party extensions
|
||||
yield sysconfig.get_config_var('LIBDIR')
|
||||
|
||||
def finalize_options(self) -> None: # noqa: C901
|
||||
def finalize_options(self): # noqa: C901
|
||||
from distutils import sysconfig
|
||||
|
||||
self.set_undefined_options(
|
||||
@@ -180,7 +153,7 @@ class build_ext(Command):
|
||||
# Make sure Python's include directories (for Python.h, pyconfig.h,
|
||||
# etc.) are in the include search path.
|
||||
py_include = sysconfig.get_python_inc()
|
||||
plat_py_include = sysconfig.get_python_inc(plat_specific=True)
|
||||
plat_py_include = sysconfig.get_python_inc(plat_specific=1)
|
||||
if self.include_dirs is None:
|
||||
self.include_dirs = self.distribution.include_dirs or []
|
||||
if isinstance(self.include_dirs, str):
|
||||
@@ -217,7 +190,7 @@ class build_ext(Command):
|
||||
# for extensions under windows use different directories
|
||||
# for Release and Debug builds.
|
||||
# also Python's library directory must be appended to library_dirs
|
||||
if os.name == 'nt' and not is_mingw():
|
||||
if os.name == 'nt':
|
||||
# the 'libs' directory is for binary installs - we assume that
|
||||
# must be the *native* platform. But we don't really support
|
||||
# cross-compiling via a binary install anyway, so we let it go.
|
||||
@@ -259,7 +232,16 @@ class build_ext(Command):
|
||||
# building python standard extensions
|
||||
self.library_dirs.append('.')
|
||||
|
||||
self.library_dirs.extend(self._python_lib_dir(sysconfig))
|
||||
# For building extensions with a shared Python library,
|
||||
# Python's library directory must be appended to library_dirs
|
||||
# See Issues: #1600860, #4366
|
||||
if sysconfig.get_config_var('Py_ENABLE_SHARED'):
|
||||
if not sysconfig.python_build:
|
||||
# building third party extensions
|
||||
self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
|
||||
else:
|
||||
# building python standard extensions
|
||||
self.library_dirs.append('.')
|
||||
|
||||
# The argument parsing will result in self.define being a string, but
|
||||
# it has to be a list of 2-tuples. All the preprocessor symbols
|
||||
@@ -297,7 +279,9 @@ class build_ext(Command):
|
||||
except ValueError:
|
||||
raise DistutilsOptionError("parallel should be an integer")
|
||||
|
||||
def run(self) -> None: # noqa: C901
|
||||
def run(self): # noqa: C901
|
||||
from distutils.ccompiler import new_compiler
|
||||
|
||||
# 'self.extensions', as supplied by setup.py, is a list of
|
||||
# Extension instances. See the documentation for Extension (in
|
||||
# distutils.extension) for details.
|
||||
@@ -336,12 +320,6 @@ class build_ext(Command):
|
||||
if os.name == 'nt' and self.plat_name != get_platform():
|
||||
self.compiler.initialize(self.plat_name)
|
||||
|
||||
# The official Windows free threaded Python installer doesn't set
|
||||
# Py_GIL_DISABLED because its pyconfig.h is shared with the
|
||||
# default build, so define it here (pypa/setuptools#4662).
|
||||
if os.name == 'nt' and is_freethreaded():
|
||||
self.compiler.define_macro('Py_GIL_DISABLED', '1')
|
||||
|
||||
# And make sure that any compile/link-related options (which might
|
||||
# come from the command-line or from the setup script) are set in
|
||||
# that CCompiler object -- that way, they automatically apply to
|
||||
@@ -350,7 +328,7 @@ class build_ext(Command):
|
||||
self.compiler.set_include_dirs(self.include_dirs)
|
||||
if self.define is not None:
|
||||
# 'define' option is a list of (name,value) tuples
|
||||
for name, value in self.define:
|
||||
for (name, value) in self.define:
|
||||
self.compiler.define_macro(name, value)
|
||||
if self.undef is not None:
|
||||
for macro in self.undef:
|
||||
@@ -367,7 +345,7 @@ class build_ext(Command):
|
||||
# Now actually compile and link everything.
|
||||
self.build_extensions()
|
||||
|
||||
def check_extensions_list(self, extensions) -> None: # noqa: C901
|
||||
def check_extensions_list(self, extensions): # noqa: C901
|
||||
"""Ensure that the list of extensions (presumably provided as a
|
||||
command option 'extensions') is valid, i.e. it is a list of
|
||||
Extension objects. We also support the old-style list of 2-tuples,
|
||||
@@ -395,7 +373,7 @@ class build_ext(Command):
|
||||
|
||||
ext_name, build_info = ext
|
||||
|
||||
log.warning(
|
||||
log.warn(
|
||||
"old-style (ext_name, build_info) tuple found in "
|
||||
"ext_modules for extension '%s' "
|
||||
"-- please convert to Extension instance",
|
||||
@@ -435,7 +413,7 @@ class build_ext(Command):
|
||||
# Medium-easy stuff: same syntax/semantics, different names.
|
||||
ext.runtime_library_dirs = build_info.get('rpath')
|
||||
if 'def_file' in build_info:
|
||||
log.warning("'def_file' element of build info dict no longer supported")
|
||||
log.warn("'def_file' element of build info dict " "no longer supported")
|
||||
|
||||
# Non-trivial stuff: 'macros' split into 'define_macros'
|
||||
# and 'undef_macros'.
|
||||
@@ -446,7 +424,8 @@ class build_ext(Command):
|
||||
for macro in macros:
|
||||
if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
|
||||
raise DistutilsSetupError(
|
||||
"'macros' element of build info dict must be 1- or 2-tuple"
|
||||
"'macros' element of build info dict "
|
||||
"must be 1- or 2-tuple"
|
||||
)
|
||||
if len(macro) == 1:
|
||||
ext.undef_macros.append(macro[0])
|
||||
@@ -473,9 +452,12 @@ class build_ext(Command):
|
||||
# And build the list of output (built) filenames. Note that this
|
||||
# ignores the 'inplace' flag, and assumes everything goes in the
|
||||
# "build" tree.
|
||||
return [self.get_ext_fullpath(ext.name) for ext in self.extensions]
|
||||
outputs = []
|
||||
for ext in self.extensions:
|
||||
outputs.append(self.get_ext_fullpath(ext.name))
|
||||
return outputs
|
||||
|
||||
def build_extensions(self) -> None:
|
||||
def build_extensions(self):
|
||||
# First, sanity-check the 'extensions' list
|
||||
self.check_extensions_list(self.extensions)
|
||||
if self.parallel:
|
||||
@@ -516,15 +498,15 @@ class build_ext(Command):
|
||||
except (CCompilerError, DistutilsError, CompileError) as e:
|
||||
if not ext.optional:
|
||||
raise
|
||||
self.warn(f'building extension "{ext.name}" failed: {e}')
|
||||
self.warn('building extension "{}" failed: {}'.format(ext.name, e))
|
||||
|
||||
def build_extension(self, ext) -> None:
|
||||
def build_extension(self, ext):
|
||||
sources = ext.sources
|
||||
if sources is None or not isinstance(sources, (list, tuple)):
|
||||
raise DistutilsSetupError(
|
||||
f"in 'ext_modules' option (extension '{ext.name}'), "
|
||||
"in 'ext_modules' option (extension '%s'), "
|
||||
"'sources' must be present and must be "
|
||||
"a list of source filenames"
|
||||
"a list of source filenames" % ext.name
|
||||
)
|
||||
# sort to make the resulting .so file build reproducible
|
||||
sources = sorted(sources)
|
||||
@@ -615,7 +597,7 @@ class build_ext(Command):
|
||||
# the temp dir.
|
||||
|
||||
if self.swig_cpp:
|
||||
log.warning("--swig-cpp is deprecated - use --swig-opts=-c++")
|
||||
log.warn("--swig-cpp is deprecated - use --swig-opts=-c++")
|
||||
|
||||
if (
|
||||
self.swig_cpp
|
||||
@@ -646,7 +628,8 @@ class build_ext(Command):
|
||||
|
||||
# Do not override commandline arguments
|
||||
if not self.swig_opts:
|
||||
swig_cmd.extend(extension.swig_opts)
|
||||
for o in extension.swig_opts:
|
||||
swig_cmd.append(o)
|
||||
|
||||
for source in swig_sources:
|
||||
target = swig_targets[source]
|
||||
@@ -667,19 +650,20 @@ class build_ext(Command):
|
||||
# Windows (or so I presume!). If we find it there, great;
|
||||
# if not, act like Unix and assume it's in the PATH.
|
||||
for vers in ("1.3", "1.2", "1.1"):
|
||||
fn = os.path.join(f"c:\\swig{vers}", "swig.exe")
|
||||
fn = os.path.join("c:\\swig%s" % vers, "swig.exe")
|
||||
if os.path.isfile(fn):
|
||||
return fn
|
||||
else:
|
||||
return "swig.exe"
|
||||
else:
|
||||
raise DistutilsPlatformError(
|
||||
f"I don't know how to find (much less run) SWIG on platform '{os.name}'"
|
||||
"I don't know how to find (much less run) SWIG "
|
||||
"on platform '%s'" % os.name
|
||||
)
|
||||
|
||||
# -- Name generators -----------------------------------------------
|
||||
# (extension names, filenames, whatever)
|
||||
def get_ext_fullpath(self, ext_name: str) -> str:
|
||||
def get_ext_fullpath(self, ext_name):
|
||||
"""Returns the path of the filename for a given extension.
|
||||
|
||||
The file is located in `build_lib` or directly in the package
|
||||
@@ -706,7 +690,7 @@ class build_ext(Command):
|
||||
# package_dir/filename
|
||||
return os.path.join(package_dir, filename)
|
||||
|
||||
def get_ext_fullname(self, ext_name: str) -> str:
|
||||
def get_ext_fullname(self, ext_name):
|
||||
"""Returns the fullname of a given extension name.
|
||||
|
||||
Adds the `package.` prefix"""
|
||||
@@ -715,27 +699,27 @@ class build_ext(Command):
|
||||
else:
|
||||
return self.package + '.' + ext_name
|
||||
|
||||
def get_ext_filename(self, ext_name: str) -> str:
|
||||
def get_ext_filename(self, ext_name):
|
||||
r"""Convert the name of an extension (eg. "foo.bar") into the name
|
||||
of the file from which it will be loaded (eg. "foo/bar.so", or
|
||||
"foo\bar.pyd").
|
||||
"""
|
||||
from ..sysconfig import get_config_var
|
||||
from distutils.sysconfig import get_config_var
|
||||
|
||||
ext_path = ext_name.split('.')
|
||||
ext_suffix = get_config_var('EXT_SUFFIX')
|
||||
return os.path.join(*ext_path) + ext_suffix
|
||||
|
||||
def get_export_symbols(self, ext: Extension) -> list[str]:
|
||||
def get_export_symbols(self, ext):
|
||||
"""Return the list of symbols that a shared extension has to
|
||||
export. This either uses 'ext.export_symbols' or, if it's not
|
||||
provided, "PyInit_" + module_name. Only relevant on Windows, where
|
||||
the .pyd file (DLL) must export the module "PyInit_" function.
|
||||
"""
|
||||
name = self._get_module_name_for_symbol(ext)
|
||||
name = ext.name.split('.')[-1]
|
||||
try:
|
||||
# Unicode module name support as defined in PEP-489
|
||||
# https://peps.python.org/pep-0489/#export-hook-name
|
||||
# https://www.python.org/dev/peps/pep-0489/#export-hook-name
|
||||
name.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii')
|
||||
@@ -747,16 +731,7 @@ class build_ext(Command):
|
||||
ext.export_symbols.append(initfunc_name)
|
||||
return ext.export_symbols
|
||||
|
||||
def _get_module_name_for_symbol(self, ext):
|
||||
# Package name should be used for `__init__` modules
|
||||
# https://github.com/python/cpython/issues/80074
|
||||
# https://github.com/pypa/setuptools/issues/4826
|
||||
parts = ext.name.split(".")
|
||||
if parts[-1] == "__init__" and len(parts) >= 2:
|
||||
return parts[-2]
|
||||
return parts[-1]
|
||||
|
||||
def get_libraries(self, ext: Extension) -> list[str]: # noqa: C901
|
||||
def get_libraries(self, ext): # noqa: C901
|
||||
"""Return the list of libraries to link against when building a
|
||||
shared extension. On most platforms, this is just 'ext.libraries';
|
||||
on Windows, we add the Python library (eg. python20.dll).
|
||||
@@ -766,8 +741,8 @@ class build_ext(Command):
|
||||
# pyconfig.h that MSVC groks. The other Windows compilers all seem
|
||||
# to need it mentioned explicitly, though, so that's what we do.
|
||||
# Append '_d' to the python import library on debug builds.
|
||||
if sys.platform == "win32" and not is_mingw():
|
||||
from .._msvccompiler import MSVCCompiler
|
||||
if sys.platform == "win32":
|
||||
from distutils._msvccompiler import MSVCCompiler
|
||||
|
||||
if not isinstance(self.compiler, MSVCCompiler):
|
||||
template = "python%d%d"
|
||||
@@ -789,14 +764,14 @@ class build_ext(Command):
|
||||
# On Cygwin (and if required, other POSIX-like platforms based on
|
||||
# Windows like MinGW) it is simply necessary that all symbols in
|
||||
# shared libraries are resolved at link time.
|
||||
from ..sysconfig import get_config_var
|
||||
from distutils.sysconfig import get_config_var
|
||||
|
||||
link_libpython = False
|
||||
if get_config_var('Py_ENABLE_SHARED'):
|
||||
# A native build on an Android device or on Cygwin
|
||||
if hasattr(sys, 'getandroidapilevel'):
|
||||
link_libpython = True
|
||||
elif sys.platform == 'cygwin' or is_mingw():
|
||||
elif sys.platform == 'cygwin':
|
||||
link_libpython = True
|
||||
elif '_PYTHON_HOST_PLATFORM' in os.environ:
|
||||
# We are cross-compiling for one of the relevant platforms
|
||||
@@ -809,4 +784,4 @@ class build_ext(Command):
|
||||
ldversion = get_config_var('LDVERSION')
|
||||
return ext.libraries + ['python' + ldversion]
|
||||
|
||||
return ext.libraries
|
||||
return ext.libraries + py37compat.pythonlib()
|
||||
|
||||
+33
-33
@@ -2,19 +2,19 @@
|
||||
|
||||
Implements the Distutils 'build_py' command."""
|
||||
|
||||
import glob
|
||||
import importlib.util
|
||||
import os
|
||||
import importlib.util
|
||||
import sys
|
||||
from distutils._log import log
|
||||
from typing import ClassVar
|
||||
import glob
|
||||
|
||||
from ..core import Command
|
||||
from ..errors import DistutilsFileError, DistutilsOptionError
|
||||
from ..util import convert_path
|
||||
from distutils.core import Command
|
||||
from distutils.errors import DistutilsOptionError, DistutilsFileError
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
|
||||
|
||||
class build_py(Command):
|
||||
|
||||
description = "\"build\" pure Python modules (copy to build directory)"
|
||||
|
||||
user_options = [
|
||||
@@ -30,8 +30,8 @@ class build_py(Command):
|
||||
('force', 'f', "forcibly build everything (ignore file timestamps)"),
|
||||
]
|
||||
|
||||
boolean_options: ClassVar[list[str]] = ['compile', 'force']
|
||||
negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
|
||||
boolean_options = ['compile', 'force']
|
||||
negative_opt = {'no-compile': 'compile'}
|
||||
|
||||
def initialize_options(self):
|
||||
self.build_lib = None
|
||||
@@ -39,11 +39,11 @@ class build_py(Command):
|
||||
self.package = None
|
||||
self.package_data = None
|
||||
self.package_dir = None
|
||||
self.compile = False
|
||||
self.compile = 0
|
||||
self.optimize = 0
|
||||
self.force = None
|
||||
|
||||
def finalize_options(self) -> None:
|
||||
def finalize_options(self):
|
||||
self.set_undefined_options(
|
||||
'build', ('build_lib', 'build_lib'), ('force', 'force')
|
||||
)
|
||||
@@ -68,7 +68,7 @@ class build_py(Command):
|
||||
except (ValueError, AssertionError):
|
||||
raise DistutilsOptionError("optimize must be 0, 1, or 2")
|
||||
|
||||
def run(self) -> None:
|
||||
def run(self):
|
||||
# XXX copy_file by default preserves atime and mtime. IMHO this is
|
||||
# the right thing to do, but perhaps it should be an option -- in
|
||||
# particular, a site administrator might want installed files to
|
||||
@@ -96,7 +96,7 @@ class build_py(Command):
|
||||
self.build_packages()
|
||||
self.build_package_data()
|
||||
|
||||
self.byte_compile(self.get_outputs(include_bytecode=False))
|
||||
self.byte_compile(self.get_outputs(include_bytecode=0))
|
||||
|
||||
def get_data_files(self):
|
||||
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
|
||||
@@ -130,14 +130,14 @@ class build_py(Command):
|
||||
os.path.join(glob.escape(src_dir), convert_path(pattern))
|
||||
)
|
||||
# Files that match more than one pattern are only added once
|
||||
files.extend([
|
||||
fn for fn in filelist if fn not in files and os.path.isfile(fn)
|
||||
])
|
||||
files.extend(
|
||||
[fn for fn in filelist if fn not in files and os.path.isfile(fn)]
|
||||
)
|
||||
return files
|
||||
|
||||
def build_package_data(self) -> None:
|
||||
def build_package_data(self):
|
||||
"""Copy data files into build directory"""
|
||||
for _package, src_dir, build_dir, filenames in self.data_files:
|
||||
for package, src_dir, build_dir, filenames in self.data_files:
|
||||
for filename in filenames:
|
||||
target = os.path.join(build_dir, filename)
|
||||
self.mkpath(os.path.dirname(target))
|
||||
@@ -192,12 +192,12 @@ class build_py(Command):
|
||||
if package_dir != "":
|
||||
if not os.path.exists(package_dir):
|
||||
raise DistutilsFileError(
|
||||
f"package directory '{package_dir}' does not exist"
|
||||
"package directory '%s' does not exist" % package_dir
|
||||
)
|
||||
if not os.path.isdir(package_dir):
|
||||
raise DistutilsFileError(
|
||||
f"supposed package directory '{package_dir}' exists, "
|
||||
"but is not a directory"
|
||||
"supposed package directory '%s' exists, "
|
||||
"but is not a directory" % package_dir
|
||||
)
|
||||
|
||||
# Directories without __init__.py are namespace packages (PEP 420).
|
||||
@@ -212,7 +212,7 @@ class build_py(Command):
|
||||
|
||||
def check_module(self, module, module_file):
|
||||
if not os.path.isfile(module_file):
|
||||
log.warning("file %s (for module %s) not found", module_file, module)
|
||||
log.warn("file %s (for module %s) not found", module_file, module)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@@ -229,7 +229,7 @@ class build_py(Command):
|
||||
module = os.path.splitext(os.path.basename(f))[0]
|
||||
modules.append((package, module, f))
|
||||
else:
|
||||
self.debug_print(f"excluding {setup_script}")
|
||||
self.debug_print("excluding %s" % setup_script)
|
||||
return modules
|
||||
|
||||
def find_modules(self):
|
||||
@@ -265,7 +265,7 @@ class build_py(Command):
|
||||
(package_dir, checked) = packages[package]
|
||||
except KeyError:
|
||||
package_dir = self.get_package_dir(package)
|
||||
checked = False
|
||||
checked = 0
|
||||
|
||||
if not checked:
|
||||
init_py = self.check_package(package, package_dir)
|
||||
@@ -307,10 +307,10 @@ class build_py(Command):
|
||||
outfile_path = [build_dir] + list(package) + [module + ".py"]
|
||||
return os.path.join(*outfile_path)
|
||||
|
||||
def get_outputs(self, include_bytecode: bool = True) -> list[str]:
|
||||
def get_outputs(self, include_bytecode=1):
|
||||
modules = self.find_all_modules()
|
||||
outputs = []
|
||||
for package, module, _module_file in modules:
|
||||
for (package, module, module_file) in modules:
|
||||
package = package.split('.')
|
||||
filename = self.get_module_outfile(self.build_lib, package, module)
|
||||
outputs.append(filename)
|
||||
@@ -348,18 +348,18 @@ class build_py(Command):
|
||||
outfile = self.get_module_outfile(self.build_lib, package, module)
|
||||
dir = os.path.dirname(outfile)
|
||||
self.mkpath(dir)
|
||||
return self.copy_file(module_file, outfile, preserve_mode=False)
|
||||
return self.copy_file(module_file, outfile, preserve_mode=0)
|
||||
|
||||
def build_modules(self) -> None:
|
||||
def build_modules(self):
|
||||
modules = self.find_modules()
|
||||
for package, module, module_file in modules:
|
||||
for (package, module, module_file) in modules:
|
||||
# Now "build" the module -- ie. copy the source file to
|
||||
# self.build_lib (the build directory for Python source).
|
||||
# (Actually, it gets copied to the directory for this package
|
||||
# under self.build_lib.)
|
||||
self.build_module(module, module_file, package)
|
||||
|
||||
def build_packages(self) -> None:
|
||||
def build_packages(self):
|
||||
for package in self.packages:
|
||||
# Get list of (package, module, module_file) tuples based on
|
||||
# scanning the package directory. 'package' is only included
|
||||
@@ -375,16 +375,16 @@ class build_py(Command):
|
||||
|
||||
# Now loop over the modules we found, "building" each one (just
|
||||
# copy it to self.build_lib).
|
||||
for package_, module, module_file in modules:
|
||||
for (package_, module, module_file) in modules:
|
||||
assert package == package_
|
||||
self.build_module(module, module_file, package)
|
||||
|
||||
def byte_compile(self, files) -> None:
|
||||
def byte_compile(self, files):
|
||||
if sys.dont_write_bytecode:
|
||||
self.warn('byte-compiling is disabled, skipping.')
|
||||
return
|
||||
|
||||
from ..util import byte_compile
|
||||
from distutils.util import byte_compile
|
||||
|
||||
prefix = self.build_lib
|
||||
if prefix[-1] != os.sep:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user