This commit is contained in:
ton
2024-10-07 10:13:40 +07:00
parent aa1631742f
commit 3a7d696db6
9729 changed files with 1832837 additions and 161742 deletions

View File

@@ -1,22 +1,37 @@
"""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 _distutils_hack.override # noqa: F401
import distutils.core
from distutils.errors import DistutilsOptionError
from distutils.util import convert_path as _convert_path
from . import logging, monkey
from . import version as _version_module
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
__all__ = [
'setup',
'Distribution',
@@ -28,11 +43,10 @@ __all__ = [
'find_namespace_packages',
]
__version__ = _version_module.__version__
_CommandT = TypeVar("_CommandT", bound="_Command")
bootstrap_install_from = None
find_packages = PackageFinder.find
find_namespace_packages = PEP420PackageFinder.find
@@ -46,7 +60,7 @@ def _install_setup_requires(attrs):
fetch_build_eggs interface.
"""
def __init__(self, attrs):
def __init__(self, attrs: Mapping[str, object]):
_incl = 'dependency_links', 'setup_requires'
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
super().__init__(filtered)
@@ -57,9 +71,9 @@ def _install_setup_requires(attrs):
"""Ignore ``pyproject.toml``, they are not related to setup_requires"""
try:
cfg, toml = super()._split_standard_project_metadata(filenames)
return cfg, ()
except Exception:
return filenames, ()
return cfg, ()
def finalize_options(self):
"""
@@ -97,16 +111,21 @@ def _fetch_build_eggs(dist):
def setup(**attrs):
# Make sure we have any requirements needed to interpret 'attrs'.
logging.configure()
# Make sure we have any requirements needed to interpret 'attrs'.
_install_setup_requires(attrs)
return distutils.core.setup(**attrs)
setup.__doc__ = distutils.core.setup.__doc__
if TYPE_CHECKING:
from typing_extensions import TypeAlias
_Command = monkey.get_unpatched(distutils.core.Command)
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
_Command: TypeAlias = distutils.core.Command
else:
_Command = monkey.get_unpatched(distutils.core.Command)
class Command(_Command):
@@ -122,42 +141,25 @@ 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:
.. 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).
:meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`.
A useful analogy for command classes is to think of them as subroutines with local
variables called "options". The options are "declared" in ``initialize_options()``
and "defined" (given their final values, aka "finalized") in ``finalize_options()``,
variables called "options". The options are "declared" in :meth:`initialize_options`
and "defined" (given their final values, aka "finalized") in :meth:`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 ``run()`` method.
Between ``initialize_options()`` and ``finalize_options()``, ``setuptools`` may set
(where it does all the work) is the :meth:`run` method.
Between :meth:`initialize_options` and :meth:`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
``finalize_options`` unless necessary.
:meth:`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 ``initialize_options()`` and
``finalize_options()``. For example, all derived attributes (or attributes that
Therefore it is important to consistently implement :meth:`initialize_options` and
:meth:`finalize_options`. For example, all derived attributes (or attributes that
depend on the value of other attributes) **SHOULD** be recomputed in
``finalize_options``.
:meth:`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,8 +167,9 @@ class Command(_Command):
"""
command_consumes_arguments = False
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
def __init__(self, dist, **kw):
def __init__(self, dist: Distribution, **kw):
"""
Construct the command for dist, updating
vars(self) with any keyword parameters.
@@ -212,11 +215,48 @@ class Command(_Command):
"'%s' must be a list of strings (got %r)" % (option, val)
)
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
@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:
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
vars(cmd).update(kw)
return cmd
@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
def _find_all_simple(path):
"""
@@ -242,22 +282,6 @@ def findall(dir=os.curdir):
return list(files)
@functools.wraps(_convert_path)
def convert_path(pathname):
SetuptoolsDeprecationWarning.emit(
"Access to implementation detail",
"""
The function `convert_path` is not provided by setuptools itself,
and therefore not part of the public API.
Its direct usage by 3rd-party packages is considered improper and the function
may be removed in the future.
""",
due_date=(2023, 12, 13), # initial deprecation 2022-03-25, see #3201
)
return _convert_path(pathname)
class sic(str):
"""Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""

View File

@@ -3,22 +3,26 @@ 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 typing import Optional, List
from distutils.util import rfc822_escape
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 .extern.packaging.markers import Marker
from .extern.packaging.requirements import Requirement
from .extern.packaging.version import Version
from .warnings import SetuptoolsDeprecationWarning
from distutils.util import rfc822_escape
def get_metadata_version(self):
mv = getattr(self, 'metadata_version', None)
@@ -36,7 +40,7 @@ def rfc822_unescape(content: str) -> str:
return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
def _read_field_from_msg(msg: Message, field: str) -> Optional[str]:
def _read_field_from_msg(msg: Message, field: str) -> str | None:
"""Read Message header field."""
value = msg[field]
if value == 'UNKNOWN':
@@ -44,7 +48,7 @@ def _read_field_from_msg(msg: Message, field: str) -> Optional[str]:
return value
def _read_field_unescaped_from_msg(msg: Message, field: str) -> Optional[str]:
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:
@@ -52,7 +56,7 @@ def _read_field_unescaped_from_msg(msg: Message, field: str) -> Optional[str]:
return rfc822_unescape(value)
def _read_list_from_msg(msg: Message, field: str) -> Optional[List[str]]:
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 == []:
@@ -60,8 +64,8 @@ def _read_list_from_msg(msg: Message, field: str) -> Optional[List[str]]:
return values
def _read_payload_from_msg(msg: Message) -> Optional[str]:
value = msg.get_payload().strip()
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
@@ -256,3 +260,27 @@ def _write_provides_extra(file, processed_extras, safe, unsafe):
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),
)

View File

@@ -1,5 +1,5 @@
import sys
import importlib
import sys
__version__, _, _ = sys.version.partition(' ')

View File

@@ -1,194 +0,0 @@
import collections
import functools
import itertools
import operator
# from jaraco.collections 3.5.1
class DictStack(list, collections.abc.Mapping):
"""
A stack of dictionaries that behaves as a view on those dictionaries,
giving preference to the last.
>>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)])
>>> stack['a']
2
>>> stack['b']
2
>>> stack['c']
2
>>> len(stack)
3
>>> stack.push(dict(a=3))
>>> stack['a']
3
>>> set(stack.keys()) == set(['a', 'b', 'c'])
True
>>> set(stack.items()) == set([('a', 3), ('b', 2), ('c', 2)])
True
>>> dict(**stack) == dict(stack) == dict(a=3, c=2, b=2)
True
>>> d = stack.pop()
>>> stack['a']
2
>>> d = stack.pop()
>>> stack['a']
1
>>> stack.get('b', None)
>>> 'c' in stack
True
"""
def __iter__(self):
dicts = list.__iter__(self)
return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts)))
def __getitem__(self, key):
for scope in reversed(tuple(list.__iter__(self))):
if key in scope:
return scope[key]
raise KeyError(key)
push = list.append
def __contains__(self, other):
return collections.abc.Mapping.__contains__(self, other)
def __len__(self):
return len(list(iter(self)))
# from jaraco.collections 3.7
class RangeMap(dict):
"""
A dictionary-like object that uses the keys as bounds for a range.
Inclusion of the value for that range is determined by the
key_match_comparator, which defaults to less-than-or-equal.
A value is returned for a key if it is the first key that matches in
the sorted list of keys.
One may supply keyword parameters to be passed to the sort function used
to sort keys (i.e. key, reverse) as sort_params.
Let's create a map that maps 1-3 -> 'a', 4-6 -> 'b'
>>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')
Even float values should work so long as the comparison operator
supports it.
>>> r[4.5]
'b'
But you'll notice that the way rangemap is defined, it must be open-ended
on one side.
>>> r[0]
'a'
>>> r[-1]
'a'
One can close the open-end of the RangeMap by using undefined_value
>>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
>>> r[0]
Traceback (most recent call last):
...
KeyError: 0
One can get the first or last elements in the range by using RangeMap.Item
>>> last_item = RangeMap.Item(-1)
>>> r[last_item]
'b'
.last_item is a shortcut for Item(-1)
>>> r[RangeMap.last_item]
'b'
Sometimes it's useful to find the bounds for a RangeMap
>>> r.bounds()
(0, 6)
RangeMap supports .get(key, default)
>>> r.get(0, 'not found')
'not found'
>>> r.get(7, 'not found')
'not found'
One often wishes to define the ranges by their left-most values,
which requires use of sort params and a key_match_comparator.
>>> r = RangeMap({1: 'a', 4: 'b'},
... sort_params=dict(reverse=True),
... key_match_comparator=operator.ge)
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')
That wasn't nearly as easy as before, so an alternate constructor
is provided:
>>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')
"""
def __init__(self, source, sort_params={}, key_match_comparator=operator.le):
dict.__init__(self, source)
self.sort_params = sort_params
self.match = key_match_comparator
@classmethod
def left(cls, source):
return cls(
source, sort_params=dict(reverse=True), key_match_comparator=operator.ge
)
def __getitem__(self, item):
sorted_keys = sorted(self.keys(), **self.sort_params)
if isinstance(item, RangeMap.Item):
result = self.__getitem__(sorted_keys[item])
else:
key = self._find_first_match_(sorted_keys, item)
result = dict.__getitem__(self, key)
if result is RangeMap.undefined_value:
raise KeyError(key)
return result
def get(self, key, default=None):
"""
Return the value for key if key is in the dictionary, else default.
If default is not given, it defaults to None, so that this method
never raises a KeyError.
"""
try:
return self[key]
except KeyError:
return default
def _find_first_match_(self, keys, item):
is_match = functools.partial(self.match, item)
matches = list(filter(is_match, keys))
if matches:
return matches[0]
raise KeyError(item)
def bounds(self):
sorted_keys = sorted(self.keys(), **self.sort_params)
return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item])
# some special values for the RangeMap
undefined_value = type('RangeValueUndefined', (), {})()
class Item(int):
"RangeMap Item"
first_item = Item(0)
last_item = Item(-1)

View File

@@ -1,20 +0,0 @@
import functools
# from jaraco.functools 3.5
def pass_none(func):
"""
Wrap func so it's not called if its first param is None
>>> print_text = pass_none(print)
>>> print_text('text')
text
>>> print_text(None)
"""
@functools.wraps(func)
def wrapper(param, *args, **kwargs):
if param is not None:
return func(param, *args, **kwargs)
return wrapper

View File

@@ -1,4 +1,3 @@
import logging
log = logging.getLogger()

View File

@@ -1,5 +1,5 @@
import sys
import importlib
import sys
def bypass_compiler_fixup(cmd, args):

View File

@@ -0,0 +1,73 @@
"""Timestamp comparison of files and groups of files."""
import functools
import os.path
from jaraco.functools import splat
from .compat.py39 import zip_strict
from .errors import DistutilsFileError
def _newer(source, target):
return not os.path.exists(target) or (
os.path.getmtime(source) > os.path.getmtime(target)
)
def newer(source, target):
"""
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)}' does not exist")
return _newer(source, target)
def newer_pairwise(sources, targets, newer=newer):
"""
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, target, missing='error'):
"""
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)

View File

@@ -3,8 +3,7 @@
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.
This module requires VS 2015 or later.
"""
# Written by Perry Stoll
@@ -13,27 +12,27 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler.
# ported to VS 2005 and VS 2008 by Christian Heimes
# ported to VS 2015 by Steve Dower
import contextlib
import os
import subprocess
import contextlib
import warnings
import unittest.mock as mock
import warnings
with contextlib.suppress(ImportError):
import winreg
from itertools import count
from ._log import log
from .ccompiler import CCompiler, gen_lib_options
from .errors import (
CompileError,
DistutilsExecError,
DistutilsPlatformError,
CompileError,
LibError,
LinkError,
)
from .ccompiler import CCompiler, gen_lib_options
from ._log import log
from .util import get_platform
from itertools import count
from .util import get_host_platform, get_platform
def _find_vc2015():
@@ -79,32 +78,40 @@ def _find_vc2017():
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
variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64'
suitable_components = (
f"Microsoft.VisualStudio.Component.VC.Tools.{variant}",
"Microsoft.VisualStudio.Workload.WDExpress",
)
path = os.path.join(path, "VC", "Auxiliary", "Build")
if os.path.isdir(path):
return 15, path
for component in suitable_components:
# Workaround for `-requiresAny` (only available on VS 2017 > 15.6)
with contextlib.suppress(
subprocess.CalledProcessError, OSError, UnicodeDecodeError
):
path = (
subprocess.check_output([
os.path.join(
root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
),
"-latest",
"-prerelease",
"-requires",
component,
"-property",
"installationPath",
"-products",
"*",
])
.decode(encoding="mbcs", errors="strict")
.strip()
)
return None, None
path = os.path.join(path, "VC", "Auxiliary", "Build")
if os.path.isdir(path):
return 15, path
return None, None # no suitable component found
PLAT_SPEC_TO_RUNTIME = {
@@ -140,7 +147,11 @@ def _get_vc_env(plat_spec):
vcvarsall, _ = _find_vcvarsall(plat_spec)
if not vcvarsall:
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
raise DistutilsPlatformError(
'Microsoft Visual C++ 14.0 or greater is required. '
'Get it with "Microsoft C++ Build Tools": '
'https://visualstudio.microsoft.com/visual-cpp-build-tools/'
)
try:
out = subprocess.check_output(
@@ -178,17 +189,43 @@ def _find_exe(exe, paths=None):
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 = {
_vcvars_names = {
'win32': 'x86',
'win-amd64': 'x86_amd64',
'win-arm32': 'x86_arm',
'win-arm64': 'x86_arm64',
'win-amd64': 'amd64',
'win-arm32': 'arm',
'win-arm64': 'arm64',
}
def _get_vcvars_spec(host_platform, platform):
"""
Given a host platform and platform, determine the spec for vcvarsall.
Uses the native MSVC host if the host platform would need expensive
emulation for x86.
>>> _get_vcvars_spec('win-arm64', 'win32')
'arm64_x86'
>>> _get_vcvars_spec('win-arm64', 'win-amd64')
'arm64_amd64'
Otherwise, always cross-compile from x86 to work with the
lighter-weight MSVC installs that do not include native 64-bit tools.
>>> _get_vcvars_spec('win32', 'win32')
'x86'
>>> _get_vcvars_spec('win-arm32', 'win-arm32')
'x86_arm'
>>> _get_vcvars_spec('win-amd64', 'win-arm64')
'x86_arm64'
"""
if host_platform != 'win-arm64':
host_platform = 'win32'
vc_hp = _vcvars_names[host_platform]
vc_plat = _vcvars_names[platform]
return vc_hp if vc_hp == vc_plat else f'{vc_hp}_{vc_plat}'
class MSVCCompiler(CCompiler):
"""Concrete class that implements an interface to Microsoft Visual C++,
as defined by the CCompiler abstract class."""
@@ -218,7 +255,7 @@ class MSVCCompiler(CCompiler):
static_lib_format = shared_lib_format = '%s%s'
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
def __init__(self, verbose=False, dry_run=False, force=False):
super().__init__(verbose, dry_run, force)
# target platform (.plat_name is consistent with 'bdist')
self.plat_name = None
@@ -242,18 +279,17 @@ class MSVCCompiler(CCompiler):
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:
if plat_name not in _vcvars_names:
raise DistutilsPlatformError(
f"--plat-name must be one of {tuple(PLAT_TO_VCVARS)}"
f"--plat-name must be one of {tuple(_vcvars_names)}"
)
# Get the vcvarsall.bat spec for the requested platform.
plat_spec = PLAT_TO_VCVARS[plat_name]
plat_spec = _get_vcvars_spec(get_host_platform(), plat_name)
vc_env = _get_vc_env(plat_spec)
if not vc_env:
raise DistutilsPlatformError(
"Unable to find a compatible " "Visual Studio installation."
"Unable to find a compatible Visual Studio installation."
)
self._configure(vc_env)
@@ -334,7 +370,7 @@ class MSVCCompiler(CCompiler):
output_dir=None,
macros=None,
include_dirs=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
depends=None,
@@ -430,7 +466,7 @@ class MSVCCompiler(CCompiler):
return objects
def create_static_lib(
self, objects, output_libname, output_dir=None, debug=0, target_lang=None
self, objects, output_libname, output_dir=None, debug=False, target_lang=None
):
if not self.initialized:
self.initialize()
@@ -459,7 +495,7 @@ class MSVCCompiler(CCompiler):
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
@@ -558,7 +594,7 @@ class MSVCCompiler(CCompiler):
def library_option(self, lib):
return self.library_filename(lib)
def find_library_file(self, dirs, lib, debug=0):
def find_library_file(self, dirs, lib, debug=False):
# Prefer a debugging library if found (and requested), but deal
# with it if we don't have one.
if debug:

View File

@@ -4,8 +4,6 @@ Utility functions for creating archive files (tarballs, zip files,
that sort of thing)."""
import os
from warnings import warn
import sys
try:
import zipfile
@@ -13,10 +11,10 @@ except ImportError:
zipfile = None
from ._log import log
from .dir_util import mkpath
from .errors import DistutilsExecError
from .spawn import spawn
from .dir_util import mkpath
from ._log import log
try:
from pwd import getpwnam
@@ -56,13 +54,18 @@ def _get_uid(name):
def make_tarball(
base_name, base_dir, compress="gzip", verbose=0, dry_run=0, owner=None, group=None
base_name,
base_dir,
compress="gzip",
verbose=False,
dry_run=False,
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", "compress", or
None. ("compress" will be deprecated in Python 3.2)
'compress' must be "gzip" (the default), "bzip2", "xz", or None.
'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
@@ -78,20 +81,17 @@ def make_tarball(
'bzip2': 'bz2',
'xz': 'xz',
None: '',
'compress': '',
}
compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz'}
# 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' or 'compress'"
"bad value for 'compress': must be None, 'gzip', 'bzip2', 'xz'"
)
archive_name = base_name + '.tar'
if compress != 'compress':
archive_name += compress_ext.get(compress, '')
archive_name += compress_ext.get(compress, '')
mkpath(os.path.dirname(archive_name), dry_run=dry_run)
@@ -113,28 +113,16 @@ def make_tarball(
return tarinfo
if not dry_run:
tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
tar = tarfile.open(archive_name, f'w|{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(base_name, base_dir, verbose=0, dry_run=0): # noqa: C901
def make_zipfile(base_name, base_dir, verbose=False, dry_run=False): # 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
@@ -160,12 +148,9 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): # noqa: C901
# XXX really should distinguish between "couldn't find
# external 'zip' command" and "zip failed".
raise DistutilsExecError(
(
"unable to create zip file '%s': "
"could neither import the 'zipfile' module nor "
"find a standalone zip utility"
)
% zip_filename
f"unable to create zip file '{zip_filename}': "
"could neither import the 'zipfile' module nor "
"find a standalone zip utility"
)
else:
@@ -224,8 +209,8 @@ def make_archive(
format,
root_dir=None,
base_dir=None,
verbose=0,
dry_run=0,
verbose=False,
dry_run=False,
owner=None,
group=None,
):
@@ -260,11 +245,10 @@ def make_archive(
try:
format_info = ARCHIVE_FORMATS[format]
except KeyError:
raise ValueError("unknown archive format '%s'" % format)
raise ValueError(f"unknown archive format '{format}'")
func = format_info[0]
for arg, val in format_info[1]:
kwargs[arg] = val
kwargs.update(format_info[1])
if format != 'zip':
kwargs['owner'] = owner

View File

@@ -1,401 +0,0 @@
"""distutils.bcppcompiler
Contains BorlandCCompiler, an implementation of the abstract CCompiler class
for the Borland C++ compiler.
"""
# This implementation by Lyle Johnson, based on the original msvccompiler.py
# module and using the directions originally published by Gordon Williams.
# XXX looks like there's a LOT of overlap between these two classes:
# someone should sit down and factor out the common code as
# WindowsCCompiler! --GPW
import os
import warnings
from .errors import (
DistutilsExecError,
CompileError,
LibError,
LinkError,
UnknownFileError,
)
from .ccompiler import CCompiler, gen_preprocess_options
from .file_util import write_file
from .dep_util import newer
from ._log import log
warnings.warn(
"bcppcompiler is deprecated and slated to be removed "
"in the future. Please discontinue use or file an issue "
"with pypa/distutils describing your use case.",
DeprecationWarning,
)
class BCPPCompiler(CCompiler):
"""Concrete class that implements an interface to the Borland C/C++
compiler, as defined by the CCompiler abstract class.
"""
compiler_type = 'bcpp'
# 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']
# Needed for the filename generation methods provided by the
# base class, CCompiler.
src_extensions = _c_extensions + _cpp_extensions
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)
# These executables are assumed to all be in the path.
# Borland doesn't seem to use any special registry settings to
# indicate their installation locations.
self.cc = "bcc32.exe"
self.linker = "ilink32.exe"
self.lib = "tlib.exe"
self.preprocess_options = None
self.compile_options = ['/tWM', '/O2', '/q', '/g0']
self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
self.ldflags_static = []
self.ldflags_exe = ['/Gn', '/q', '/x']
self.ldflags_exe_debug = ['/Gn', '/q', '/x', '/r']
# -- Worker methods ------------------------------------------------
def compile( # noqa: C901
self,
sources,
output_dir=None,
macros=None,
include_dirs=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
depends=None,
):
macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
output_dir, macros, include_dirs, sources, depends, extra_postargs
)
compile_opts = extra_preargs or []
compile_opts.append('-c')
if debug:
compile_opts.extend(self.compile_options_debug)
else:
compile_opts.extend(self.compile_options)
for obj in objects:
try:
src, ext = build[obj]
except KeyError:
continue
# XXX why do the normpath here?
src = os.path.normpath(src)
obj = os.path.normpath(obj)
# XXX _setup_compile() did a mkpath() too but before the normpath.
# Is it possible to skip the normpath?
self.mkpath(os.path.dirname(obj))
if ext == '.res':
# This is already a binary file -- skip it.
continue # the 'for' loop
if ext == '.rc':
# This needs to be compiled to a .res file -- do it now.
try:
self.spawn(["brcc32", "-fo", obj, src])
except DistutilsExecError as msg:
raise CompileError(msg)
continue # the 'for' loop
# The next two are both for the real compiler.
if ext in self._c_extensions:
input_opt = ""
elif ext in self._cpp_extensions:
input_opt = "-P"
else:
# Unknown file type -- no extra options. The compiler
# will probably fail, but let it just in case this is a
# file the compiler recognizes even if we don't.
input_opt = ""
output_opt = "-o" + obj
# Compiler command line syntax is: "bcc32 [options] file(s)".
# Note that the source file names must appear at the end of
# the command line.
try:
self.spawn(
[self.cc]
+ compile_opts
+ pp_opts
+ [input_opt, output_opt]
+ extra_postargs
+ [src]
)
except DistutilsExecError as msg:
raise CompileError(msg)
return objects
# compile ()
def create_static_lib(
self, objects, output_libname, output_dir=None, debug=0, target_lang=None
):
(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 = [output_filename, '/u'] + objects
if debug:
pass # XXX what goes here?
try:
self.spawn([self.lib] + lib_args)
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
# create_static_lib ()
def link( # noqa: C901
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
# XXX this ignores 'build_temp'! should follow the lead of
# msvccompiler.py
(objects, output_dir) = self._fix_object_args(objects, output_dir)
(libraries, library_dirs, runtime_library_dirs) = self._fix_lib_args(
libraries, library_dirs, runtime_library_dirs
)
if runtime_library_dirs:
log.warning(
"I don't know what to do with 'runtime_library_dirs': %s",
str(runtime_library_dirs),
)
if output_dir is not None:
output_filename = os.path.join(output_dir, output_filename)
if self._need_link(objects, output_filename):
# Figure out linker args based on type of target.
if target_desc == CCompiler.EXECUTABLE:
startup_obj = 'c0w32'
if debug:
ld_args = self.ldflags_exe_debug[:]
else:
ld_args = self.ldflags_exe[:]
else:
startup_obj = 'c0d32'
if debug:
ld_args = self.ldflags_shared_debug[:]
else:
ld_args = self.ldflags_shared[:]
# Create a temporary exports file for use by the linker
if export_symbols is None:
def_file = ''
else:
head, tail = os.path.split(output_filename)
modname, ext = os.path.splitext(tail)
temp_dir = os.path.dirname(objects[0]) # preserve tree structure
def_file = os.path.join(temp_dir, '%s.def' % modname)
contents = ['EXPORTS']
for sym in export_symbols or []:
contents.append(' {}=_{}'.format(sym, sym))
self.execute(write_file, (def_file, contents), "writing %s" % def_file)
# Borland C++ has problems with '/' in paths
objects2 = map(os.path.normpath, objects)
# split objects in .obj and .res files
# Borland C++ needs them at different positions in the command line
objects = [startup_obj]
resources = []
for file in objects2:
(base, ext) = os.path.splitext(os.path.normcase(file))
if ext == '.res':
resources.append(file)
else:
objects.append(file)
for ell in library_dirs:
ld_args.append("/L%s" % os.path.normpath(ell))
ld_args.append("/L.") # we sometimes use relative paths
# list of object files
ld_args.extend(objects)
# XXX the command-line syntax for Borland C++ is a bit wonky;
# certain filenames are jammed together in one big string, but
# comma-delimited. This doesn't mesh too well with the
# Unix-centric attitude (with a DOS/Windows quoting hack) of
# 'spawn()', so constructing the argument list is a bit
# awkward. Note that doing the obvious thing and jamming all
# the filenames and commas into one argument would be wrong,
# because 'spawn()' would quote any filenames with spaces in
# them. Arghghh!. Apparently it works fine as coded...
# name of dll/exe file
ld_args.extend([',', output_filename])
# no map file and start libraries
ld_args.append(',,')
for lib in libraries:
# see if we find it and if there is a bcpp specific lib
# (xxx_bcpp.lib)
libfile = self.find_library_file(library_dirs, lib, debug)
if libfile is None:
ld_args.append(lib)
# probably a BCPP internal library -- don't warn
else:
# full name which prefers bcpp_xxx.lib over xxx.lib
ld_args.append(libfile)
# some default libraries
ld_args.extend(('import32', 'cw32mt'))
# def file for export symbols
ld_args.extend([',', def_file])
# add resource files
ld_args.append(',')
ld_args.extend(resources)
if extra_preargs:
ld_args[:0] = extra_preargs
if extra_postargs:
ld_args.extend(extra_postargs)
self.mkpath(os.path.dirname(output_filename))
try:
self.spawn([self.linker] + ld_args)
except DistutilsExecError as msg:
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
# link ()
# -- Miscellaneous methods -----------------------------------------
def find_library_file(self, dirs, lib, debug=0):
# List of effective library names to try, in order of preference:
# xxx_bcpp.lib is better than xxx.lib
# and xxx_d.lib is better than xxx.lib if debug is set
#
# The "_bcpp" suffix is to handle a Python installation for people
# with multiple compilers (primarily Distutils hackers, I suspect
# ;-). The idea is they'd have one static library for each
# compiler they care about, since (almost?) every Windows compiler
# seems to have a different format for static libraries.
if debug:
dlib = lib + "_d"
try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
else:
try_names = (lib + "_bcpp", lib)
for dir in dirs:
for name in try_names:
libfile = os.path.join(dir, self.library_filename(name))
if os.path.exists(libfile):
return libfile
else:
# Oops, didn't find it in *any* of 'dirs'
return None
# overwrite the one from CCompiler to support rc and res-files
def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
if output_dir is None:
output_dir = ''
obj_names = []
for src_name in source_filenames:
# use normcase to make sure '.rc' is really '.rc' and not '.RC'
(base, ext) = os.path.splitext(os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc', '.res']):
raise UnknownFileError(
"unknown file type '{}' (from '{}')".format(ext, src_name)
)
if strip_dir:
base = os.path.basename(base)
if ext == '.res':
# these can go unchanged
obj_names.append(os.path.join(output_dir, base + ext))
elif ext == '.rc':
# these need to be compiled to .res-files
obj_names.append(os.path.join(output_dir, base + '.res'))
else:
obj_names.append(os.path.join(output_dir, base + self.obj_extension))
return obj_names
# object_filenames ()
def preprocess(
self,
source,
output_file=None,
macros=None,
include_dirs=None,
extra_preargs=None,
extra_postargs=None,
):
(_, macros, include_dirs) = self._fix_compile_args(None, macros, include_dirs)
pp_opts = gen_preprocess_options(macros, include_dirs)
pp_args = ['cpp32.exe'] + pp_opts
if output_file is not None:
pp_args.append('-o' + output_file)
if extra_preargs:
pp_args[:0] = extra_preargs
if extra_postargs:
pp_args.extend(extra_postargs)
pp_args.append(source)
# We need to preprocess: either we're being forced to, or the
# source file is newer than the target (or the target doesn't
# exist).
if self.force or output_file is None or newer(source, output_file):
if output_file:
self.mkpath(os.path.dirname(output_file))
try:
self.spawn(pp_args)
except DistutilsExecError as msg:
print(msg)
raise CompileError(msg)
# preprocess()

View File

@@ -3,24 +3,27 @@
Contains CCompiler, an abstract base class that defines the interface
for the Distutils compiler abstraction model."""
import sys
import os
import re
import sys
import types
import warnings
from more_itertools import always_iterable
from ._log import log
from ._modified import newer_group
from .dir_util import mkpath
from .errors import (
CompileError,
DistutilsModuleError,
DistutilsPlatformError,
LinkError,
UnknownFileError,
DistutilsPlatformError,
DistutilsModuleError,
)
from .spawn import spawn
from .file_util import move_file
from .dir_util import mkpath
from .dep_util import newer_group
from .util import split_quoted, execute
from ._log import log
from .spawn import spawn
from .util import execute, is_mingw, split_quoted
class CCompiler:
@@ -103,7 +106,7 @@ class CCompiler:
library dirs specific to this compiler class
"""
def __init__(self, verbose=0, dry_run=0, force=0):
def __init__(self, verbose=False, dry_run=False, force=False):
self.dry_run = dry_run
self.force = force
self.verbose = verbose
@@ -168,8 +171,7 @@ class CCompiler:
for key in kwargs:
if key not in self.executables:
raise ValueError(
"unknown executable '%s' for class %s"
% (key, self.__class__.__name__)
f"unknown executable '{key}' for class {self.__class__.__name__}"
)
self.set_executable(key, kwargs[key])
@@ -188,24 +190,28 @@ class CCompiler:
return None
def _check_macro_definitions(self, definitions):
"""Ensures that every element of 'definitions' is a valid macro
definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do
nothing if all definitions are OK, raise TypeError otherwise.
"""
"""Ensure that every element of 'definitions' is valid."""
for defn in definitions:
if not (
isinstance(defn, tuple)
and (
len(defn) in (1, 2)
and (isinstance(defn[1], str) or defn[1] is None)
)
and isinstance(defn[0], str)
):
raise TypeError(
("invalid macro definition '%s': " % defn)
+ "must be tuple (string,), (string, string), or "
+ "(string, None)"
)
self._check_macro_definition(*defn)
def _check_macro_definition(self, defn):
"""
Raise a TypeError if defn is not valid.
A valid definition is either a (name, value) 2-tuple or a (name,) tuple.
"""
if not isinstance(defn, tuple) or not self._is_valid_macro(*defn):
raise TypeError(
f"invalid macro definition '{defn}': "
"must be tuple (string,), (string, string), or (string, None)"
)
@staticmethod
def _is_valid_macro(name, value=None):
"""
A valid macro is a ``name : str`` and a ``value : str | None``.
"""
return isinstance(name, str) and isinstance(value, (str, types.NoneType))
# -- Bookkeeping methods -------------------------------------------
@@ -342,7 +348,7 @@ class CCompiler:
extra = []
# Get the list of expected output (object) files
objects = self.object_filenames(sources, strip_dir=0, output_dir=outdir)
objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)
assert len(objects) == len(sources)
pp_opts = gen_preprocess_options(macros, incdirs)
@@ -382,7 +388,7 @@ class CCompiler:
raise TypeError("'output_dir' must be a string or None")
if macros is None:
macros = self.macros
macros = list(self.macros)
elif isinstance(macros, list):
macros = macros + (self.macros or [])
else:
@@ -441,14 +447,14 @@ class CCompiler:
fixed versions of all arguments.
"""
if libraries is None:
libraries = self.libraries
libraries = list(self.libraries)
elif isinstance(libraries, (list, tuple)):
libraries = list(libraries) + (self.libraries or [])
else:
raise TypeError("'libraries' (if supplied) must be a list of strings")
if library_dirs is None:
library_dirs = self.library_dirs
library_dirs = list(self.library_dirs)
elif isinstance(library_dirs, (list, tuple)):
library_dirs = list(library_dirs) + (self.library_dirs or [])
else:
@@ -458,14 +464,14 @@ class CCompiler:
library_dirs += self.__class__.library_dirs
if runtime_library_dirs is None:
runtime_library_dirs = self.runtime_library_dirs
runtime_library_dirs = list(self.runtime_library_dirs)
elif isinstance(runtime_library_dirs, (list, tuple)):
runtime_library_dirs = list(runtime_library_dirs) + (
self.runtime_library_dirs or []
)
else:
raise TypeError(
"'runtime_library_dirs' (if supplied) " "must be a list of strings"
"'runtime_library_dirs' (if supplied) must be a list of strings"
)
return (libraries, library_dirs, runtime_library_dirs)
@@ -532,7 +538,7 @@ class CCompiler:
output_dir=None,
macros=None,
include_dirs=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
depends=None,
@@ -609,7 +615,7 @@ class CCompiler:
pass
def create_static_lib(
self, objects, output_libname, output_dir=None, debug=0, target_lang=None
self, objects, output_libname, output_dir=None, debug=False, target_lang=None
):
"""Link a bunch of stuff together to create a static library file.
The "bunch of stuff" consists of the list of object files supplied
@@ -650,7 +656,7 @@ class CCompiler:
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
@@ -712,7 +718,7 @@ class CCompiler:
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
@@ -743,7 +749,7 @@ class CCompiler:
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
@@ -773,7 +779,7 @@ class CCompiler:
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
debug=0,
debug=False,
extra_preargs=None,
extra_postargs=None,
target_lang=None,
@@ -857,10 +863,9 @@ class CCompiler:
if library_dirs is None:
library_dirs = []
fd, fname = tempfile.mkstemp(".c", funcname, text=True)
f = os.fdopen(fd, "w")
try:
with os.fdopen(fd, "w", encoding='utf-8') as f:
for incl in includes:
f.write("""#include "%s"\n""" % incl)
f.write(f"""#include "{incl}"\n""")
if not includes:
# Use "char func(void);" as the prototype to follow
# what autoconf does. This prototype does not match
@@ -870,25 +875,22 @@ class CCompiler:
# know the exact argument types, and the has_function
# interface does not provide that level of information.
f.write(
"""\
f"""\
#ifdef __cplusplus
extern "C"
#endif
char %s(void);
char {funcname}(void);
"""
% funcname
)
f.write(
"""\
int main (int argc, char **argv) {
%s();
f"""\
int main (int argc, char **argv) {{
{funcname}();
return 0;
}
}}
"""
% funcname
)
finally:
f.close()
try:
objects = self.compile([fname], include_dirs=include_dirs)
except CompileError:
@@ -911,7 +913,7 @@ int main (int argc, char **argv) {
os.remove(fn)
return True
def find_library_file(self, dirs, lib, debug=0):
def find_library_file(self, dirs, lib, debug=False):
"""Search the specified list of directories for a static or shared
library file 'lib' and return the full path to that file. If
'debug' true, look for a debugging version (if that makes sense on
@@ -954,7 +956,7 @@ int main (int argc, char **argv) {
# * exe_extension -
# extension for executable files, eg. '' or '.exe'
def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
if output_dir is None:
output_dir = ''
return list(
@@ -972,9 +974,7 @@ int main (int argc, char **argv) {
try:
new_ext = self.out_extensions[ext]
except LookupError:
raise UnknownFileError(
"unknown file type '{}' (from '{}')".format(ext, src_name)
)
raise UnknownFileError(f"unknown file type '{ext}' (from '{src_name}')")
if strip_dir:
base = os.path.basename(base)
return os.path.join(output_dir, base + new_ext)
@@ -991,20 +991,24 @@ int main (int argc, char **argv) {
# If abs, chop off leading /
return no_drive[os.path.isabs(no_drive) :]
def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
def shared_object_filename(self, basename, strip_dir=False, output_dir=''):
assert output_dir is not None
if strip_dir:
basename = os.path.basename(basename)
return os.path.join(output_dir, basename + self.shared_lib_extension)
def executable_filename(self, basename, strip_dir=0, output_dir=''):
def executable_filename(self, basename, strip_dir=False, output_dir=''):
assert output_dir is not None
if strip_dir:
basename = os.path.basename(basename)
return os.path.join(output_dir, basename + (self.exe_extension or ''))
def library_filename(
self, libname, lib_type='static', strip_dir=0, output_dir='' # or 'shared'
self,
libname,
lib_type='static',
strip_dir=False,
output_dir='', # or 'shared'
):
assert output_dir is not None
expected = '"static", "shared", "dylib", "xcode_stub"'
@@ -1032,7 +1036,7 @@ int main (int argc, char **argv) {
print(msg)
def warn(self, msg):
sys.stderr.write("warning: %s\n" % msg)
sys.stderr.write(f"warning: {msg}\n")
def execute(self, func, args, msg=None, level=1):
execute(func, args, msg, self.dry_run)
@@ -1056,6 +1060,7 @@ _default_compilers = (
# on a cygwin built python we can use gcc like an ordinary UNIXish
# compiler
('cygwin.*', 'unix'),
('zos', 'zos'),
# OS name mappings
('posix', 'unix'),
('nt', 'msvc'),
@@ -1076,6 +1081,10 @@ def get_default_compiler(osname=None, platform=None):
osname = os.name
if platform is None:
platform = sys.platform
# Mingw is a special case where sys.platform is 'win32' but we
# want to use the 'mingw32' compiler, so check it first
if is_mingw():
return 'mingw32'
for pattern, compiler in _default_compilers:
if (
re.match(pattern, platform) is not None
@@ -1103,6 +1112,7 @@ compiler_class = {
"Mingw32 port of GNU C Compiler for Win32",
),
'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
}
@@ -1115,15 +1125,15 @@ def show_compilers():
# commands that use it.
from distutils.fancy_getopt import FancyGetopt
compilers = []
for compiler in compiler_class.keys():
compilers.append(("compiler=" + compiler, None, compiler_class[compiler][2]))
compilers.sort()
compilers = sorted(
("compiler=" + compiler, None, compiler_class[compiler][2])
for compiler in compiler_class.keys()
)
pretty_printer = FancyGetopt(compilers)
pretty_printer.print_help("List of available compilers:")
def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
def new_compiler(plat=None, compiler=None, verbose=False, dry_run=False, force=False):
"""Generate an instance of some CCompiler subclass for the supplied
platform/compiler combination. 'plat' defaults to 'os.name'
(eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
@@ -1143,9 +1153,9 @@ def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
(module_name, class_name, long_description) = compiler_class[compiler]
except KeyError:
msg = "don't know how to compile C/C++ code on platform '%s'" % plat
msg = f"don't know how to compile C/C++ code on platform '{plat}'"
if compiler is not None:
msg = msg + " with '%s' compiler" % compiler
msg = msg + f" with '{compiler}' compiler"
raise DistutilsPlatformError(msg)
try:
@@ -1155,12 +1165,12 @@ def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
klass = vars(module)[class_name]
except ImportError:
raise DistutilsModuleError(
"can't compile C/C++ code: unable to load module '%s'" % module_name
f"can't compile C/C++ code: unable to load module '{module_name}'"
)
except KeyError:
raise DistutilsModuleError(
"can't compile C/C++ code: unable to find class '%s' "
"in module '%s'" % (class_name, module_name)
f"can't compile C/C++ code: unable to find class '{class_name}' "
f"in module '{module_name}'"
)
# XXX The None is necessary to preserve backwards compatibility
@@ -1194,23 +1204,22 @@ def gen_preprocess_options(macros, include_dirs):
for macro in macros:
if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
raise TypeError(
"bad macro definition '%s': "
"each element of 'macros' list must be a 1- or 2-tuple" % macro
f"bad macro definition '{macro}': "
"each element of 'macros' list must be a 1- or 2-tuple"
)
if len(macro) == 1: # undefine this macro
pp_opts.append("-U%s" % macro[0])
pp_opts.append(f"-U{macro[0]}")
elif len(macro) == 2:
if macro[1] is None: # define with no explicit value
pp_opts.append("-D%s" % macro[0])
pp_opts.append(f"-D{macro[0]}")
else:
# XXX *don't* need to be clever about quoting the
# macro value here, because we're going to avoid the
# shell at all costs when we spawn the command!
pp_opts.append("-D%s=%s" % macro)
pp_opts.append("-D{}={}".format(*macro))
for dir in include_dirs:
pp_opts.append("-I%s" % dir)
pp_opts.extend(f"-I{dir}" for dir in include_dirs)
return pp_opts
@@ -1221,17 +1230,10 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
directories. Returns a list of command-line options suitable for use
with some compiler (depending on the two format strings passed in).
"""
lib_opts = []
for dir in library_dirs:
lib_opts.append(compiler.library_dir_option(dir))
lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
for dir in runtime_library_dirs:
opt = compiler.runtime_library_dir_option(dir)
if isinstance(opt, list):
lib_opts = lib_opts + opt
else:
lib_opts.append(opt)
lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
# XXX it's important that we *not* remove redundant library mentions!
# sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
@@ -1247,7 +1249,7 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
lib_opts.append(lib_file)
else:
compiler.warn(
"no library file corresponding to " "'%s' found (skipping)" % lib
f"no library file corresponding to '{lib}' found (skipping)"
)
else:
lib_opts.append(compiler.library_option(lib))

View File

@@ -4,14 +4,14 @@ Provides the Command class, the base class for the command classes
in the distutils.command package.
"""
import sys
import logging
import os
import re
import logging
import sys
from .errors import DistutilsOptionError
from . import util, dir_util, file_util, archive_util, dep_util
from . import _modified, archive_util, dir_util, file_util, util
from ._log import log
from .errors import DistutilsOptionError
class Command:
@@ -87,13 +87,13 @@ class Command:
# The 'help' flag is just used for command-line parsing, so
# none of that complicated bureaucracy is needed.
self.help = 0
self.help = False
# '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 = 0
self.finalized = False
# XXX A more explicit way to customize dry_run would be better.
def __getattr__(self, attr):
@@ -109,7 +109,7 @@ class Command:
def ensure_finalized(self):
if not self.finalized:
self.finalize_options()
self.finalized = 1
self.finalized = True
# Subclasses must define:
# initialize_options()
@@ -135,7 +135,7 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError(
"abstract method -- subclass %s must override" % self.__class__
f"abstract method -- subclass {self.__class__} must override"
)
def finalize_options(self):
@@ -150,14 +150,14 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError(
"abstract method -- subclass %s must override" % self.__class__
f"abstract method -- subclass {self.__class__} must override"
)
def dump_options(self, header=None, indent=""):
from distutils.fancy_getopt import longopt_xlate
if header is None:
header = "command options for '%s':" % self.get_command_name()
header = f"command options for '{self.get_command_name()}':"
self.announce(indent + header, level=logging.INFO)
indent = indent + " "
for option, _, _ in self.user_options:
@@ -165,7 +165,7 @@ class Command:
if option[-1] == "=":
option = option[:-1]
value = getattr(self, option)
self.announce(indent + "{} = {}".format(option, value), level=logging.INFO)
self.announce(indent + f"{option} = {value}", level=logging.INFO)
def run(self):
"""A command's raison d'etre: carry out the action it exists to
@@ -178,7 +178,7 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError(
"abstract method -- subclass %s must override" % self.__class__
f"abstract method -- subclass {self.__class__} must override"
)
def announce(self, msg, level=logging.DEBUG):
@@ -213,9 +213,7 @@ class Command:
setattr(self, option, default)
return default
elif not isinstance(val, str):
raise DistutilsOptionError(
"'{}' must be a {} (got `{}`)".format(option, what, val)
)
raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
return val
def ensure_string(self, option, default=None):
@@ -242,7 +240,7 @@ class Command:
ok = False
if not ok:
raise DistutilsOptionError(
"'{}' must be a list of strings (got {!r})".format(option, val)
f"'{option}' must be a list of strings (got {val!r})"
)
def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
@@ -295,7 +293,7 @@ class Command:
if getattr(self, dst_option) is None:
setattr(self, dst_option, getattr(src_cmd_obj, src_option))
def get_finalized_command(self, command, create=1):
def get_finalized_command(self, command, create=True):
"""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
@@ -307,7 +305,7 @@ class Command:
# XXX rename to 'get_reinitialized_command()'? (should do the
# same in dist.py, if so)
def reinitialize_command(self, command, reinit_subcommands=0):
def reinitialize_command(self, command, reinit_subcommands=False):
return self.distribution.reinitialize_command(command, reinit_subcommands)
def run_command(self, command):
@@ -342,7 +340,13 @@ class Command:
dir_util.mkpath(name, mode, dry_run=self.dry_run)
def copy_file(
self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1
self,
infile,
outfile,
preserve_mode=True,
preserve_times=True,
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
@@ -361,9 +365,9 @@ class Command:
self,
infile,
outfile,
preserve_mode=1,
preserve_times=1,
preserve_symlinks=0,
preserve_mode=True,
preserve_times=True,
preserve_symlinks=False,
level=1,
):
"""Copy an entire directory tree respecting verbose, dry-run,
@@ -383,7 +387,7 @@ class Command:
"""Move a file respecting dry-run flag."""
return file_util.move_file(src, dst, dry_run=self.dry_run)
def spawn(self, cmd, search_path=1, level=1):
def spawn(self, cmd, search_path=True, level=1):
"""Spawn an external command respecting dry-run flag."""
from distutils.spawn import spawn
@@ -414,7 +418,7 @@ class Command:
timestamp checks.
"""
if skip_msg is None:
skip_msg = "skipping %s (inputs unchanged)" % outfile
skip_msg = f"skipping {outfile} (inputs unchanged)"
# Allow 'infiles' to be a single string
if isinstance(infiles, str):
@@ -428,7 +432,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 dep_util.newer_group(infiles, outfile):
if self.force or _modified.newer_group(infiles, outfile):
self.execute(func, args, exec_msg, level)
# Otherwise, print the "skip" message
else:

View File

@@ -3,7 +3,7 @@
Package containing implementation of all the standard Distutils
commands."""
__all__ = [ # noqa: F822
__all__ = [
'build',
'build_py',
'build_ext',
@@ -16,10 +16,8 @@ __all__ = [ # noqa: F822
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'check',
'upload',
]

Some files were not shown because too many files have changed in this diff Show More