aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers')
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py1394
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py340
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py24
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py614
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py83
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py76
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py48
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py136
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py350
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py423
-rw-r--r--.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py230
11 files changed, 3718 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py
new file mode 100644
index 00000000..5efd2a39
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/base.py
@@ -0,0 +1,1394 @@
+"""distutils.ccompiler
+
+Contains Compiler, an abstract base class that defines the interface
+for the Distutils compiler abstraction model."""
+
+from __future__ import annotations
+
+import os
+import pathlib
+import re
+import sys
+import warnings
+from collections.abc import Callable, Iterable, MutableSequence, Sequence
+from typing import (
+ TYPE_CHECKING,
+ ClassVar,
+ Literal,
+ TypeVar,
+ Union,
+ overload,
+)
+
+from more_itertools import always_iterable
+
+from ..._log import log
+from ..._modified import newer_group
+from ...dir_util import mkpath
+from ...errors import (
+ DistutilsModuleError,
+ DistutilsPlatformError,
+)
+from ...file_util import move_file
+from ...spawn import spawn
+from ...util import execute, is_mingw, split_quoted
+from .errors import (
+ CompileError,
+ LinkError,
+ UnknownFileType,
+)
+
+if TYPE_CHECKING:
+ from typing_extensions import TypeAlias, TypeVarTuple, Unpack
+
+ _Ts = TypeVarTuple("_Ts")
+
+_Macro: TypeAlias = Union[tuple[str], tuple[str, Union[str, None]]]
+_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
+_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
+
+
+class Compiler:
+ """Abstract base class to define the interface that must be implemented
+ by real compiler classes. Also has some utility methods used by
+ several compiler classes.
+
+ The basic idea behind a compiler abstraction class is that each
+ instance can be used for all the compile/link steps in building a
+ single project. Thus, attributes common to all of those compile and
+ link steps -- include directories, macros to define, libraries to link
+ against, etc. -- are attributes of the compiler instance. To allow for
+ variability in how individual files are treated, most of those
+ attributes may be varied on a per-compilation or per-link basis.
+ """
+
+ # 'compiler_type' is a class attribute that identifies this class. It
+ # keeps code that wants to know what kind of compiler it's dealing with
+ # from having to import all possible compiler classes just to do an
+ # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type'
+ # should really, really be one of the keys of the 'compiler_class'
+ # dictionary (see below -- used by the 'new_compiler()' factory
+ # function) -- authors of new compiler interface classes are
+ # responsible for updating 'compiler_class'!
+ compiler_type: ClassVar[str] = None # type: ignore[assignment]
+
+ # XXX things not handled by this compiler abstraction model:
+ # * client can't provide additional options for a compiler,
+ # e.g. warning, optimization, debugging flags. Perhaps this
+ # should be the domain of concrete compiler abstraction classes
+ # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
+ # class should have methods for the common ones.
+ # * can't completely override the include or library searchg
+ # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
+ # I'm not sure how widely supported this is even by Unix
+ # compilers, much less on other platforms. And I'm even less
+ # sure how useful it is; maybe for cross-compiling, but
+ # support for that is a ways off. (And anyways, cross
+ # compilers probably have a dedicated binary with the
+ # right paths compiled in. I hope.)
+ # * can't do really freaky things with the library list/library
+ # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
+ # different versions of libfoo.a in different locations. I
+ # think this is useless without the ability to null out the
+ # library search path anyways.
+
+ executables: ClassVar[dict]
+
+ # Subclasses that rely on the standard filename generation methods
+ # implemented below should override these; see the comment near
+ # those methods ('object_filenames()' et. al.) for details:
+ src_extensions: ClassVar[list[str] | None] = None
+ obj_extension: ClassVar[str | None] = None
+ static_lib_extension: ClassVar[str | None] = None
+ shared_lib_extension: ClassVar[str | None] = None
+ static_lib_format: ClassVar[str | None] = None # format string
+ shared_lib_format: ClassVar[str | None] = None # prob. same as static_lib_format
+ exe_extension: ClassVar[str | None] = None
+
+ # Default language settings. language_map is used to detect a source
+ # file or Extension target language, checking source filenames.
+ # language_order is used to detect the language precedence, when deciding
+ # what language to use when mixing source types. For example, if some
+ # extension has two files with ".c" extension, and one with ".cpp", it
+ # is still linked as c++.
+ language_map: ClassVar[dict[str, str]] = {
+ ".c": "c",
+ ".cc": "c++",
+ ".cpp": "c++",
+ ".cxx": "c++",
+ ".m": "objc",
+ }
+ language_order: ClassVar[list[str]] = ["c++", "objc", "c"]
+
+ include_dirs: list[str] = []
+ """
+ include dirs specific to this compiler class
+ """
+
+ library_dirs: list[str] = []
+ """
+ library dirs specific to this compiler class
+ """
+
+ def __init__(
+ self, verbose: bool = False, dry_run: bool = False, force: bool = False
+ ) -> None:
+ self.dry_run = dry_run
+ self.force = force
+ self.verbose = verbose
+
+ # 'output_dir': a common output directory for object, library,
+ # shared object, and shared library files
+ self.output_dir: str | None = None
+
+ # 'macros': a list of macro definitions (or undefinitions). A
+ # macro definition is a 2-tuple (name, value), where the value is
+ # either a string or None (no explicit value). A macro
+ # undefinition is a 1-tuple (name,).
+ self.macros: list[_Macro] = []
+
+ # 'include_dirs': a list of directories to search for include files
+ self.include_dirs = []
+
+ # 'libraries': a list of libraries to include in any link
+ # (library names, not filenames: eg. "foo" not "libfoo.a")
+ self.libraries: list[str] = []
+
+ # 'library_dirs': a list of directories to search for libraries
+ self.library_dirs = []
+
+ # 'runtime_library_dirs': a list of directories to search for
+ # shared libraries/objects at runtime
+ self.runtime_library_dirs: list[str] = []
+
+ # 'objects': a list of object files (or similar, such as explicitly
+ # named library files) to include on any link
+ self.objects: list[str] = []
+
+ for key in self.executables.keys():
+ self.set_executable(key, self.executables[key])
+
+ def set_executables(self, **kwargs: str) -> None:
+ """Define the executables (and options for them) that will be run
+ to perform the various stages of compilation. The exact set of
+ executables that may be specified here depends on the compiler
+ class (via the 'executables' class attribute), but most will have:
+ compiler the C/C++ compiler
+ linker_so linker used to create shared objects and libraries
+ linker_exe linker used to create binary executables
+ archiver static library creator
+
+ On platforms with a command-line (Unix, DOS/Windows), each of these
+ is a string that will be split into executable name and (optional)
+ list of arguments. (Splitting the string is done similarly to how
+ Unix shells operate: words are delimited by spaces, but quotes and
+ backslashes can override this. See
+ 'distutils.util.split_quoted()'.)
+ """
+
+ # Note that some CCompiler implementation classes will define class
+ # attributes 'cpp', 'cc', etc. with hard-coded executable names;
+ # this is appropriate when a compiler class is for exactly one
+ # compiler/OS combination (eg. MSVCCompiler). Other compiler
+ # classes (UnixCCompiler, in particular) are driven by information
+ # discovered at run-time, since there are many different ways to do
+ # basically the same things with Unix C compilers.
+
+ for key in kwargs:
+ if key not in self.executables:
+ raise ValueError(
+ f"unknown executable '{key}' for class {self.__class__.__name__}"
+ )
+ self.set_executable(key, kwargs[key])
+
+ def set_executable(self, key, value):
+ if isinstance(value, str):
+ setattr(self, key, split_quoted(value))
+ else:
+ setattr(self, key, value)
+
+ def _find_macro(self, name):
+ i = 0
+ for defn in self.macros:
+ if defn[0] == name:
+ return i
+ i += 1
+ return None
+
+ def _check_macro_definitions(self, definitions):
+ """Ensure that every element of 'definitions' is valid."""
+ for defn in definitions:
+ 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``.
+
+ >>> Compiler._is_valid_macro('foo', None)
+ True
+ """
+ return isinstance(name, str) and isinstance(value, (str, type(None)))
+
+ # -- Bookkeeping methods -------------------------------------------
+
+ def define_macro(self, name: str, value: str | None = None) -> None:
+ """Define a preprocessor macro for all compilations driven by this
+ compiler object. The optional parameter 'value' should be a
+ string; if it is not supplied, then the macro will be defined
+ without an explicit value and the exact outcome depends on the
+ compiler used (XXX true? does ANSI say anything about this?)
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ self.macros.append((name, value))
+
+ def undefine_macro(self, name: str) -> None:
+ """Undefine a preprocessor macro for all compilations driven by
+ this compiler object. If the same macro is defined by
+ 'define_macro()' and undefined by 'undefine_macro()' the last call
+ takes precedence (including multiple redefinitions or
+ undefinitions). If the macro is redefined/undefined on a
+ per-compilation basis (ie. in the call to 'compile()'), then that
+ takes precedence.
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ undefn = (name,)
+ self.macros.append(undefn)
+
+ def add_include_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ header files. The compiler is instructed to search directories in
+ the order in which they are supplied by successive calls to
+ 'add_include_dir()'.
+ """
+ self.include_dirs.append(dir)
+
+ def set_include_dirs(self, dirs: list[str]) -> None:
+ """Set the list of directories that will be searched to 'dirs' (a
+ list of strings). Overrides any preceding calls to
+ 'add_include_dir()'; subsequence calls to 'add_include_dir()' add
+ to the list passed to 'set_include_dirs()'. This does not affect
+ any list of standard include directories that the compiler may
+ search by default.
+ """
+ self.include_dirs = dirs[:]
+
+ def add_library(self, libname: str) -> None:
+ """Add 'libname' to the list of libraries that will be included in
+ all links driven by this compiler object. Note that 'libname'
+ should *not* be the name of a file containing a library, but the
+ name of the library itself: the actual filename will be inferred by
+ the linker, the compiler, or the compiler class (depending on the
+ platform).
+
+ The linker will be instructed to link against libraries in the
+ order they were supplied to 'add_library()' and/or
+ 'set_libraries()'. It is perfectly valid to duplicate library
+ names; the linker will be instructed to link against libraries as
+ many times as they are mentioned.
+ """
+ self.libraries.append(libname)
+
+ def set_libraries(self, libnames: list[str]) -> None:
+ """Set the list of libraries to be included in all links driven by
+ this compiler object to 'libnames' (a list of strings). This does
+ not affect any standard system libraries that the linker may
+ include by default.
+ """
+ self.libraries = libnames[:]
+
+ def add_library_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ libraries specified to 'add_library()' and 'set_libraries()'. The
+ linker will be instructed to search for libraries in the order they
+ are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
+ """
+ self.library_dirs.append(dir)
+
+ def set_library_dirs(self, dirs: list[str]) -> None:
+ """Set the list of library search directories to 'dirs' (a list of
+ strings). This does not affect any standard library search path
+ that the linker may search by default.
+ """
+ self.library_dirs = dirs[:]
+
+ def add_runtime_library_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ shared libraries at runtime.
+ """
+ self.runtime_library_dirs.append(dir)
+
+ def set_runtime_library_dirs(self, dirs: list[str]) -> None:
+ """Set the list of directories to search for shared libraries at
+ runtime to 'dirs' (a list of strings). This does not affect any
+ standard search path that the runtime linker may search by
+ default.
+ """
+ self.runtime_library_dirs = dirs[:]
+
+ def add_link_object(self, object: str) -> None:
+ """Add 'object' to the list of object files (or analogues, such as
+ explicitly named library files or the output of "resource
+ compilers") to be included in every link driven by this compiler
+ object.
+ """
+ self.objects.append(object)
+
+ def set_link_objects(self, objects: list[str]) -> None:
+ """Set the list of object files (or analogues) to be included in
+ every link to 'objects'. This does not affect any standard object
+ files that the linker may include by default (such as system
+ libraries).
+ """
+ self.objects = objects[:]
+
+ # -- Private utility methods --------------------------------------
+ # (here for the convenience of subclasses)
+
+ # Helper method to prep compiler in subclass compile() methods
+
+ def _setup_compile(
+ self,
+ outdir: str | None,
+ macros: list[_Macro] | None,
+ incdirs: list[str] | tuple[str, ...] | None,
+ sources,
+ depends,
+ extra,
+ ):
+ """Process arguments and decide which source files to compile."""
+ outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
+
+ if extra is None:
+ extra = []
+
+ # Get the list of expected output (object) files
+ objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)
+ assert len(objects) == len(sources)
+
+ pp_opts = gen_preprocess_options(macros, incdirs)
+
+ build = {}
+ for i in range(len(sources)):
+ src = sources[i]
+ obj = objects[i]
+ ext = os.path.splitext(src)[1]
+ self.mkpath(os.path.dirname(obj))
+ build[obj] = (src, ext)
+
+ return macros, objects, extra, pp_opts, build
+
+ def _get_cc_args(self, pp_opts, debug, before):
+ # works for unixccompiler, cygwinccompiler
+ cc_args = pp_opts + ['-c']
+ if debug:
+ cc_args[:0] = ['-g']
+ if before:
+ cc_args[:0] = before
+ return cc_args
+
+ def _fix_compile_args(
+ self,
+ output_dir: str | None,
+ macros: list[_Macro] | None,
+ include_dirs: list[str] | tuple[str, ...] | None,
+ ) -> tuple[str, list[_Macro], list[str]]:
+ """Typecheck and fix-up some of the arguments to the 'compile()'
+ method, and return fixed-up values. Specifically: if 'output_dir'
+ is None, replaces it with 'self.output_dir'; ensures that 'macros'
+ is a list, and augments it with 'self.macros'; ensures that
+ 'include_dirs' is a list, and augments it with 'self.include_dirs'.
+ Guarantees that the returned values are of the correct type,
+ i.e. for 'output_dir' either string or None, and for 'macros' and
+ 'include_dirs' either list or None.
+ """
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ if macros is None:
+ macros = list(self.macros)
+ elif isinstance(macros, list):
+ macros = macros + (self.macros or [])
+ else:
+ raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+ if include_dirs is None:
+ include_dirs = list(self.include_dirs)
+ elif isinstance(include_dirs, (list, tuple)):
+ include_dirs = list(include_dirs) + (self.include_dirs or [])
+ else:
+ raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+
+ # add include dirs for class
+ include_dirs += self.__class__.include_dirs
+
+ return output_dir, macros, include_dirs
+
+ def _prep_compile(self, sources, output_dir, depends=None):
+ """Decide which source files must be recompiled.
+
+ Determine the list of object files corresponding to 'sources',
+ and figure out which ones really need to be recompiled.
+ Return a list of all object files and a dictionary telling
+ which source files can be skipped.
+ """
+ # Get the list of expected output (object) files
+ objects = self.object_filenames(sources, output_dir=output_dir)
+ assert len(objects) == len(sources)
+
+ # Return an empty dict for the "which source files can be skipped"
+ # return value to preserve API compatibility.
+ return objects, {}
+
+ def _fix_object_args(
+ self, objects: list[str] | tuple[str, ...], output_dir: str | None
+ ) -> tuple[list[str], str]:
+ """Typecheck and fix up some arguments supplied to various methods.
+ Specifically: ensure that 'objects' is a list; if output_dir is
+ None, replace with self.output_dir. Return fixed versions of
+ 'objects' and 'output_dir'.
+ """
+ if not isinstance(objects, (list, tuple)):
+ raise TypeError("'objects' must be a list or tuple of strings")
+ objects = list(objects)
+
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ return (objects, output_dir)
+
+ def _fix_lib_args(
+ self,
+ libraries: list[str] | tuple[str, ...] | None,
+ library_dirs: list[str] | tuple[str, ...] | None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None,
+ ) -> tuple[list[str], list[str], list[str]]:
+ """Typecheck and fix up some of the arguments supplied to the
+ 'link_*' methods. Specifically: ensure that all arguments are
+ lists, and augment them with their permanent versions
+ (eg. 'self.libraries' augments 'libraries'). Return a tuple with
+ fixed versions of all arguments.
+ """
+ if libraries is None:
+ 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 = list(self.library_dirs)
+ elif isinstance(library_dirs, (list, tuple)):
+ library_dirs = list(library_dirs) + (self.library_dirs or [])
+ else:
+ raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+
+ # add library dirs for class
+ library_dirs += self.__class__.library_dirs
+
+ if runtime_library_dirs is None:
+ 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"
+ )
+
+ return (libraries, library_dirs, runtime_library_dirs)
+
+ def _need_link(self, objects, output_file):
+ """Return true if we need to relink the files listed in 'objects'
+ to recreate 'output_file'.
+ """
+ if self.force:
+ return True
+ else:
+ if self.dry_run:
+ newer = newer_group(objects, output_file, missing='newer')
+ else:
+ newer = newer_group(objects, output_file)
+ return newer
+
+ def detect_language(self, sources: str | list[str]) -> str | None:
+ """Detect the language of a given file, or list of files. Uses
+ language_map, and language_order to do the job.
+ """
+ if not isinstance(sources, list):
+ sources = [sources]
+ lang = None
+ index = len(self.language_order)
+ for source in sources:
+ base, ext = os.path.splitext(source)
+ extlang = self.language_map.get(ext)
+ try:
+ extindex = self.language_order.index(extlang)
+ if extindex < index:
+ lang = extlang
+ index = extindex
+ except ValueError:
+ pass
+ return lang
+
+ # -- Worker methods ------------------------------------------------
+ # (must be implemented by subclasses)
+
+ def preprocess(
+ self,
+ source: str | os.PathLike[str],
+ output_file: str | os.PathLike[str] | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ ):
+ """Preprocess a single C/C++ source file, named in 'source'.
+ Output will be written to file named 'output_file', or stdout if
+ 'output_file' not supplied. 'macros' is a list of macro
+ definitions as for 'compile()', which will augment the macros set
+ with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a
+ list of directory names that will be added to the default list.
+
+ Raises PreprocessError on failure.
+ """
+ pass
+
+ def compile(
+ self,
+ sources: Sequence[str | os.PathLike[str]],
+ output_dir: str | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ depends: list[str] | tuple[str, ...] | None = None,
+ ) -> list[str]:
+ """Compile one or more source files.
+
+ 'sources' must be a list of filenames, most likely C/C++
+ files, but in reality anything that can be handled by a
+ particular compiler and compiler class (eg. MSVCCompiler can
+ handle resource files in 'sources'). Return a list of object
+ filenames, one per source filename in 'sources'. Depending on
+ the implementation, not all source files will necessarily be
+ compiled, but all corresponding object filenames will be
+ returned.
+
+ If 'output_dir' is given, object files will be put under it, while
+ retaining their original path component. That is, "foo/bar.c"
+ normally compiles to "foo/bar.o" (for a Unix implementation); if
+ 'output_dir' is "build", then it would compile to
+ "build/foo/bar.o".
+
+ 'macros', if given, must be a list of macro definitions. A macro
+ definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
+ The former defines a macro; if the value is None, the macro is
+ defined without an explicit value. The 1-tuple case undefines a
+ macro. Later definitions/redefinitions/ undefinitions take
+ precedence.
+
+ 'include_dirs', if given, must be a list of strings, the
+ directories to add to the default include file search path for this
+ compilation only.
+
+ 'debug' is a boolean; if true, the compiler will be instructed to
+ output debug symbols in (or alongside) the object file(s).
+
+ 'extra_preargs' and 'extra_postargs' are implementation- dependent.
+ On platforms that have the notion of a command-line (e.g. Unix,
+ DOS/Windows), they are most likely lists of strings: extra
+ command-line arguments to prepend/append to the compiler command
+ line. On other platforms, consult the implementation class
+ documentation. In any event, they are intended as an escape hatch
+ for those occasions when the abstract compiler framework doesn't
+ cut the mustard.
+
+ 'depends', if given, is a list of filenames that all targets
+ depend on. If a source file is older than any file in
+ depends, then the source file will be recompiled. This
+ supports dependency tracking, but only at a coarse
+ granularity.
+
+ Raises CompileError on failure.
+ """
+ # A concrete compiler class can either override this method
+ # entirely or implement _compile().
+ macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+ output_dir, macros, include_dirs, sources, depends, extra_postargs
+ )
+ cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+ # Return *all* object filenames, not just the ones we just built.
+ return objects
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compile 'src' to product 'obj'."""
+ # A concrete compiler class that does not override compile()
+ # should implement _compile().
+ pass
+
+ def create_static_lib(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ debug: bool = False,
+ target_lang: str | None = None,
+ ) -> 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
+ as 'objects', the extra object files supplied to
+ 'add_link_object()' and/or 'set_link_objects()', the libraries
+ supplied to 'add_library()' and/or 'set_libraries()', and the
+ libraries supplied as 'libraries' (if any).
+
+ 'output_libname' should be a library name, not a filename; the
+ filename will be inferred from the library name. 'output_dir' is
+ the directory where the library file will be put.
+
+ 'debug' is a boolean; if true, debugging information will be
+ included in the library (note that on most platforms, it is the
+ compile step where this matters: the 'debug' flag is included here
+ just for consistency).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LibError on failure.
+ """
+ pass
+
+ # values for target_desc parameter in link()
+ SHARED_OBJECT = "shared_object"
+ SHARED_LIBRARY = "shared_library"
+ EXECUTABLE = "executable"
+
+ def link(
+ self,
+ target_desc: str,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ """Link a bunch of stuff together to create an executable or
+ shared library file.
+
+ The "bunch of stuff" consists of the list of object files supplied
+ as 'objects'. 'output_filename' should be a filename. If
+ 'output_dir' is supplied, 'output_filename' is relative to it
+ (i.e. 'output_filename' can provide directory components if
+ needed).
+
+ 'libraries' is a list of libraries to link against. These are
+ library names, not filenames, since they're translated into
+ filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
+ on Unix and "foo.lib" on DOS/Windows). However, they can include a
+ directory component, which means the linker will look in that
+ specific directory rather than searching all the normal locations.
+
+ 'library_dirs', if supplied, should be a list of directories to
+ search for libraries that were specified as bare library names
+ (ie. no directory component). These are on top of the system
+ default and those supplied to 'add_library_dir()' and/or
+ 'set_library_dirs()'. 'runtime_library_dirs' is a list of
+ directories that will be embedded into the shared library and used
+ to search for other shared libraries that *it* depends on at
+ run-time. (This may only be relevant on Unix.)
+
+ 'export_symbols' is a list of symbols that the shared library will
+ export. (This appears to be relevant only on Windows.)
+
+ 'debug' is as for 'compile()' and 'create_static_lib()', with the
+ slight distinction that it actually matters on most platforms (as
+ opposed to 'create_static_lib()', which includes a 'debug' flag
+ mostly for form's sake).
+
+ 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
+ of course that they supply command-line arguments for the
+ particular linker being used).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LinkError on failure.
+ """
+ raise NotImplementedError
+
+ # Old 'link_*()' methods, rewritten to use the new 'link()' method.
+
+ def link_shared_lib(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.SHARED_LIBRARY,
+ objects,
+ self.library_filename(output_libname, lib_type='shared'),
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def link_shared_object(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.SHARED_OBJECT,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def link_executable(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_progname: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.EXECUTABLE,
+ objects,
+ self.executable_filename(output_progname),
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ None,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ None,
+ target_lang,
+ )
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function; there is
+ # no appropriate default implementation so subclasses should
+ # implement all of these.
+
+ def library_dir_option(self, dir: str) -> str:
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for libraries.
+ """
+ raise NotImplementedError
+
+ def runtime_library_dir_option(self, dir: str) -> str:
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for runtime libraries.
+ """
+ raise NotImplementedError
+
+ def library_option(self, lib: str) -> str:
+ """Return the compiler option to add 'lib' to the list of libraries
+ linked into the shared library or executable.
+ """
+ raise NotImplementedError
+
+ def has_function( # noqa: C901
+ self,
+ funcname: str,
+ includes: Iterable[str] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ libraries: list[str] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ ) -> bool:
+ """Return a boolean indicating whether funcname is provided as
+ a symbol on the current platform. The optional arguments can
+ be used to augment the compilation environment.
+
+ The libraries argument is a list of flags to be passed to the
+ linker to make additional symbol definitions available for
+ linking.
+
+ The includes and include_dirs arguments are deprecated.
+ Usually, supplying include files with function declarations
+ will cause function detection to fail even in cases where the
+ symbol is available for linking.
+
+ """
+ # this can't be included at module scope because it tries to
+ # import math which might not be available at that point - maybe
+ # the necessary logic should just be inlined?
+ import tempfile
+
+ if includes is None:
+ includes = []
+ else:
+ warnings.warn("includes is deprecated", DeprecationWarning)
+ if include_dirs is None:
+ include_dirs = []
+ else:
+ warnings.warn("include_dirs is deprecated", DeprecationWarning)
+ if libraries is None:
+ libraries = []
+ if library_dirs is None:
+ library_dirs = []
+ fd, fname = tempfile.mkstemp(".c", funcname, text=True)
+ with os.fdopen(fd, "w", encoding='utf-8') as f:
+ for incl in includes:
+ 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
+ # any well-known function the compiler might recognize
+ # as a builtin, so this ends up as a true link test.
+ # Without a fake prototype, the test would need to
+ # 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 {funcname}(void);
+"""
+ )
+ f.write(
+ f"""\
+int main (int argc, char **argv) {{
+ {funcname}();
+ return 0;
+}}
+"""
+ )
+
+ try:
+ objects = self.compile([fname], include_dirs=include_dirs)
+ except CompileError:
+ return False
+ finally:
+ os.remove(fname)
+
+ try:
+ self.link_executable(
+ objects, "a.out", libraries=libraries, library_dirs=library_dirs
+ )
+ except (LinkError, TypeError):
+ return False
+ else:
+ os.remove(
+ self.executable_filename("a.out", output_dir=self.output_dir or '')
+ )
+ finally:
+ for fn in objects:
+ os.remove(fn)
+ return True
+
+ def find_library_file(
+ self, dirs: Iterable[str], lib: str, debug: bool = False
+ ) -> str | None:
+ """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
+ the current platform). Return None if 'lib' wasn't found in any of
+ the specified directories.
+ """
+ raise NotImplementedError
+
+ # -- Filename generation methods -----------------------------------
+
+ # The default implementation of the filename generating methods are
+ # prejudiced towards the Unix/DOS/Windows view of the world:
+ # * object files are named by replacing the source file extension
+ # (eg. .c/.cpp -> .o/.obj)
+ # * library files (shared or static) are named by plugging the
+ # library name and extension into a format string, eg.
+ # "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+ # * executables are named by appending an extension (possibly
+ # empty) to the program name: eg. progname + ".exe" for
+ # Windows
+ #
+ # To reduce redundant code, these methods expect to find
+ # several attributes in the current object (presumably defined
+ # as class attributes):
+ # * src_extensions -
+ # list of C/C++ source file extensions, eg. ['.c', '.cpp']
+ # * obj_extension -
+ # object file extension, eg. '.o' or '.obj'
+ # * static_lib_extension -
+ # extension for static library files, eg. '.a' or '.lib'
+ # * shared_lib_extension -
+ # extension for shared library/object files, eg. '.so', '.dll'
+ # * static_lib_format -
+ # format string for generating static library filenames,
+ # eg. 'lib%s.%s' or '%s.%s'
+ # * shared_lib_format
+ # format string for generating shared library filenames
+ # (probably same as static_lib_format, since the extension
+ # is one of the intended parameters to the format string)
+ # * exe_extension -
+ # extension for executable files, eg. '' or '.exe'
+
+ def object_filenames(
+ self,
+ source_filenames: Iterable[str | os.PathLike[str]],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] | None = '',
+ ) -> list[str]:
+ if output_dir is None:
+ output_dir = ''
+ return list(
+ self._make_out_path(output_dir, strip_dir, src_name)
+ for src_name in source_filenames
+ )
+
+ @property
+ def out_extensions(self):
+ return dict.fromkeys(self.src_extensions, self.obj_extension)
+
+ def _make_out_path(self, output_dir, strip_dir, src_name):
+ return self._make_out_path_exts(
+ output_dir, strip_dir, src_name, self.out_extensions
+ )
+
+ @classmethod
+ def _make_out_path_exts(cls, output_dir, strip_dir, src_name, extensions):
+ r"""
+ >>> exts = {'.c': '.o'}
+ >>> Compiler._make_out_path_exts('.', False, '/foo/bar.c', exts).replace('\\', '/')
+ './foo/bar.o'
+ >>> Compiler._make_out_path_exts('.', True, '/foo/bar.c', exts).replace('\\', '/')
+ './bar.o'
+ """
+ src = pathlib.PurePath(src_name)
+ # Ensure base is relative to honor output_dir (python/cpython#37775).
+ base = cls._make_relative(src)
+ try:
+ new_ext = extensions[src.suffix]
+ except LookupError:
+ raise UnknownFileType(f"unknown file type '{src.suffix}' (from '{src}')")
+ if strip_dir:
+ base = pathlib.PurePath(base.name)
+ return os.path.join(output_dir, base.with_suffix(new_ext))
+
+ @staticmethod
+ def _make_relative(base: pathlib.Path):
+ return base.relative_to(base.anchor)
+
+ @overload
+ def shared_object_filename(
+ self,
+ basename: str,
+ strip_dir: Literal[False] = False,
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ @overload
+ def shared_object_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: Literal[True],
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ def shared_object_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = '',
+ ) -> str:
+ 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)
+
+ @overload
+ def executable_filename(
+ self,
+ basename: str,
+ strip_dir: Literal[False] = False,
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ @overload
+ def executable_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: Literal[True],
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ def executable_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = '',
+ ) -> str:
+ 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: str,
+ lib_type: str = "static",
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = "", # or 'shared'
+ ):
+ assert output_dir is not None
+ expected = '"static", "shared", "dylib", "xcode_stub"'
+ if lib_type not in eval(expected):
+ raise ValueError(f"'lib_type' must be {expected}")
+ fmt = getattr(self, lib_type + "_lib_format")
+ ext = getattr(self, lib_type + "_lib_extension")
+
+ dir, base = os.path.split(libname)
+ filename = fmt % (base, ext)
+ if strip_dir:
+ dir = ''
+
+ return os.path.join(output_dir, dir, filename)
+
+ # -- Utility methods -----------------------------------------------
+
+ def announce(self, msg: object, level: int = 1) -> None:
+ log.debug(msg)
+
+ def debug_print(self, msg: object) -> None:
+ from distutils.debug import DEBUG
+
+ if DEBUG:
+ print(msg)
+
+ def warn(self, msg: object) -> None:
+ sys.stderr.write(f"warning: {msg}\n")
+
+ def execute(
+ self,
+ func: Callable[[Unpack[_Ts]], object],
+ args: tuple[Unpack[_Ts]],
+ msg: object = None,
+ level: int = 1,
+ ) -> None:
+ execute(func, args, msg, self.dry_run)
+
+ def spawn(
+ self, cmd: MutableSequence[bytes | str | os.PathLike[str]], **kwargs
+ ) -> None:
+ spawn(cmd, dry_run=self.dry_run, **kwargs)
+
+ @overload
+ def move_file(
+ self, src: str | os.PathLike[str], dst: _StrPathT
+ ) -> _StrPathT | str: ...
+ @overload
+ def move_file(
+ self, src: bytes | os.PathLike[bytes], dst: _BytesPathT
+ ) -> _BytesPathT | bytes: ...
+ def move_file(
+ self,
+ src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
+ return move_file(src, dst, dry_run=self.dry_run)
+
+ def mkpath(self, name, mode=0o777):
+ mkpath(name, mode, dry_run=self.dry_run)
+
+
+# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
+# type for that platform. Keys are interpreted as re match
+# patterns. Order is important; platform mappings are preferred over
+# OS names.
+_default_compilers = (
+ # Platform string mappings
+ # 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'),
+)
+
+
+def get_default_compiler(osname: str | None = None, platform: str | None = None) -> str:
+ """Determine the default compiler to use for the given platform.
+
+ osname should be one of the standard Python OS names (i.e. the
+ ones returned by os.name) and platform the common value
+ returned by sys.platform for the platform in question.
+
+ The default values are os.name and sys.platform in case the
+ parameters are not given.
+ """
+ if osname is 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
+ or re.match(pattern, osname) is not None
+ ):
+ return compiler
+ # Default to Unix compiler
+ return 'unix'
+
+
+# Map compiler types to (module_name, class_name) pairs -- ie. where to
+# find the code that implements an interface to this compiler. (The module
+# is assumed to be in the 'distutils' package.)
+compiler_class = {
+ 'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"),
+ 'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"),
+ 'cygwin': (
+ 'cygwinccompiler',
+ 'CygwinCCompiler',
+ "Cygwin port of GNU C Compiler for Win32",
+ ),
+ 'mingw32': (
+ 'cygwinccompiler',
+ 'Mingw32CCompiler',
+ "Mingw32 port of GNU C Compiler for Win32",
+ ),
+ 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
+ 'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
+}
+
+
+def show_compilers() -> None:
+ """Print list of available compilers (used by the "--help-compiler"
+ options to "build", "build_ext", "build_clib").
+ """
+ # XXX this "knows" that the compiler option it's describing is
+ # "--compiler", which just happens to be the case for the three
+ # commands that use it.
+ from distutils.fancy_getopt import FancyGetopt
+
+ 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: str | None = None,
+ compiler: str | None = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+ force: bool = False,
+) -> Compiler:
+ """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
+ for that platform. Currently only 'posix' and 'nt' are supported, and
+ the default compilers are "traditional Unix interface" (UnixCCompiler
+ class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
+ possible to ask for a Unix compiler object under Windows, and a
+ Microsoft compiler object under Unix -- if you supply a value for
+ 'compiler', 'plat' is ignored.
+ """
+ if plat is None:
+ plat = os.name
+
+ try:
+ if compiler is None:
+ compiler = get_default_compiler(plat)
+
+ (module_name, class_name, long_description) = compiler_class[compiler]
+ except KeyError:
+ msg = f"don't know how to compile C/C++ code on platform '{plat}'"
+ if compiler is not None:
+ msg = msg + f" with '{compiler}' compiler"
+ raise DistutilsPlatformError(msg)
+
+ try:
+ module_name = "distutils." + module_name
+ __import__(module_name)
+ module = sys.modules[module_name]
+ klass = vars(module)[class_name]
+ except ImportError:
+ raise DistutilsModuleError(
+ f"can't compile C/C++ code: unable to load module '{module_name}'"
+ )
+ except KeyError:
+ raise DistutilsModuleError(
+ 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
+ # with classes that expect verbose to be the first positional
+ # argument.
+ return klass(None, dry_run, force)
+
+
+def gen_preprocess_options(
+ macros: Iterable[_Macro], include_dirs: Iterable[str]
+) -> list[str]:
+ """Generate C pre-processor options (-D, -U, -I) as used by at least
+ two types of compilers: the typical Unix compiler and Visual C++.
+ 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
+ means undefine (-U) macro 'name', and (name,value) means define (-D)
+ macro 'name' to 'value'. 'include_dirs' is just a list of directory
+ names to be added to the header file search path (-I). Returns a list
+ of command-line options suitable for either Unix compilers or Visual
+ C++.
+ """
+ # XXX it would be nice (mainly aesthetic, and so we don't generate
+ # stupid-looking command lines) to go over 'macros' and eliminate
+ # redundant definitions/undefinitions (ie. ensure that only the
+ # latest mention of a particular macro winds up on the command
+ # line). I don't think it's essential, though, since most (all?)
+ # Unix C compilers only pay attention to the latest -D or -U
+ # mention of a macro on their command line. Similar situation for
+ # 'include_dirs'. I'm punting on both for now. Anyways, weeding out
+ # redundancies like this should probably be the province of
+ # CCompiler, since the data structures used are inherited from it
+ # and therefore common to all CCompiler classes.
+ pp_opts = []
+ for macro in macros:
+ if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
+ raise TypeError(
+ 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(f"-U{macro[0]}")
+ elif len(macro) == 2:
+ if macro[1] is None: # define with no explicit value
+ 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{}={}".format(*macro))
+
+ pp_opts.extend(f"-I{dir}" for dir in include_dirs)
+ return pp_opts
+
+
+def gen_lib_options(
+ compiler: Compiler,
+ library_dirs: Iterable[str],
+ runtime_library_dirs: Iterable[str],
+ libraries: Iterable[str],
+) -> list[str]:
+ """Generate linker options for searching library directories and
+ linking with specific libraries. 'libraries' and 'library_dirs' are,
+ respectively, lists of library names (not filenames!) and search
+ directories. Returns a list of command-line options suitable for use
+ with some compiler (depending on the two format strings passed in).
+ """
+ lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
+
+ for dir in runtime_library_dirs:
+ 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
+ # resolve all symbols. I just hope we never have to say "-lfoo obj.o
+ # -lbar" to get things to work -- that's certainly a possibility, but a
+ # pretty nasty way to arrange your C code.
+
+ for lib in libraries:
+ (lib_dir, lib_name) = os.path.split(lib)
+ if lib_dir:
+ lib_file = compiler.find_library_file([lib_dir], lib_name)
+ if lib_file:
+ lib_opts.append(lib_file)
+ else:
+ compiler.warn(
+ f"no library file corresponding to '{lib}' found (skipping)"
+ )
+ else:
+ lib_opts.append(compiler.library_option(lib))
+ return lib_opts
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py
new file mode 100644
index 00000000..bfabbb30
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/cygwin.py
@@ -0,0 +1,340 @@
+"""distutils.cygwinccompiler
+
+Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
+handles the Cygwin port of the GNU C compiler to Windows. It also contains
+the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
+cygwin in no-cygwin mode).
+"""
+
+import copy
+import os
+import pathlib
+import shlex
+import sys
+import warnings
+from subprocess import check_output
+
+from ...errors import (
+ DistutilsExecError,
+ DistutilsPlatformError,
+)
+from ...file_util import write_file
+from ...sysconfig import get_config_vars
+from ...version import LooseVersion, suppress_known_deprecation
+from . import unix
+from .errors import (
+ CompileError,
+ Error,
+)
+
+
+def get_msvcr():
+ """No longer needed, but kept for backward compatibility."""
+ return []
+
+
+_runtime_library_dirs_msg = (
+ "Unable to set runtime library search path on Windows, "
+ "usually indicated by `runtime_library_dirs` parameter to Extension"
+)
+
+
+class Compiler(unix.Compiler):
+ """Handles the Cygwin port of the GNU C compiler to Windows."""
+
+ compiler_type = 'cygwin'
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".dll.a"
+ dylib_lib_extension = ".dll"
+ static_lib_format = "lib%s%s"
+ shared_lib_format = "lib%s%s"
+ dylib_lib_format = "cyg%s%s"
+ exe_extension = ".exe"
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+
+ status, details = check_config_h()
+ self.debug_print(f"Python's GCC status: {status} (details: {details})")
+ if status is not CONFIG_H_OK:
+ self.warn(
+ "Python's pyconfig.h doesn't seem to support your compiler. "
+ f"Reason: {details}. "
+ "Compiling may fail because of undefined preprocessor macros."
+ )
+
+ self.cc, self.cxx = get_config_vars('CC', 'CXX')
+
+ # Override 'CC' and 'CXX' environment variables for
+ # building using MINGW compiler for MSVC python.
+ self.cc = os.environ.get('CC', self.cc or 'gcc')
+ self.cxx = os.environ.get('CXX', self.cxx or 'g++')
+
+ self.linker_dll = self.cc
+ self.linker_dll_cxx = self.cxx
+ shared_option = "-shared"
+
+ self.set_executables(
+ compiler=f'{self.cc} -mcygwin -O -Wall',
+ compiler_so=f'{self.cc} -mcygwin -mdll -O -Wall',
+ compiler_cxx=f'{self.cxx} -mcygwin -O -Wall',
+ compiler_so_cxx=f'{self.cxx} -mcygwin -mdll -O -Wall',
+ linker_exe=f'{self.cc} -mcygwin',
+ linker_so=f'{self.linker_dll} -mcygwin {shared_option}',
+ linker_exe_cxx=f'{self.cxx} -mcygwin',
+ linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}',
+ )
+
+ self.dll_libraries = get_msvcr()
+
+ @property
+ def gcc_version(self):
+ # Older numpy depended on this existing to check for ancient
+ # gcc versions. This doesn't make much sense with clang etc so
+ # just hardcode to something recent.
+ # https://github.com/numpy/numpy/pull/20333
+ warnings.warn(
+ "gcc_version attribute of CygwinCCompiler is deprecated. "
+ "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ with suppress_known_deprecation():
+ return LooseVersion("11.2.0")
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compiles the source by spawning GCC and windres if needed."""
+ if ext in ('.rc', '.res'):
+ # gcc needs '.res' and '.rc' compiled to object files !!!
+ try:
+ self.spawn(["windres", "-i", src, "-o", obj])
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ else: # for other files use the C-compiler
+ try:
+ if self.detect_language(src) == 'c++':
+ self.spawn(
+ self.compiler_so_cxx
+ + cc_args
+ + [src, '-o', obj]
+ + extra_postargs
+ )
+ else:
+ self.spawn(
+ self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs
+ )
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def link(
+ self,
+ target_desc,
+ objects,
+ output_filename,
+ output_dir=None,
+ libraries=None,
+ library_dirs=None,
+ runtime_library_dirs=None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ """Link the objects."""
+ # use separate copies, so we can modify the lists
+ extra_preargs = copy.copy(extra_preargs or [])
+ libraries = copy.copy(libraries or [])
+ objects = copy.copy(objects or [])
+
+ if runtime_library_dirs:
+ self.warn(_runtime_library_dirs_msg)
+
+ # Additional libraries
+ libraries.extend(self.dll_libraries)
+
+ # handle export symbols by creating a def-file
+ # with executables this only works with gcc/ld as linker
+ if (export_symbols is not None) and (
+ target_desc != self.EXECUTABLE or self.linker_dll == "gcc"
+ ):
+ # (The linker doesn't do anything if output is up-to-date.
+ # So it would probably better to check if we really need this,
+ # but for this we had to insert some unchanged parts of
+ # UnixCCompiler, and this is not what we want.)
+
+ # we want to put some files in the same directory as the
+ # object files are, build_temp doesn't help much
+ # where are the object files
+ temp_dir = os.path.dirname(objects[0])
+ # name of dll to give the helper files the same base name
+ (dll_name, dll_extension) = os.path.splitext(
+ os.path.basename(output_filename)
+ )
+
+ # generate the filenames for these files
+ def_file = os.path.join(temp_dir, dll_name + ".def")
+
+ # Generate .def file
+ contents = [f"LIBRARY {os.path.basename(output_filename)}", "EXPORTS"]
+ contents.extend(export_symbols)
+ self.execute(write_file, (def_file, contents), f"writing {def_file}")
+
+ # next add options for def-file
+
+ # for gcc/ld the def-file is specified as any object files
+ objects.append(def_file)
+
+ # end: if ((export_symbols is not None) and
+ # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
+
+ # who wants symbols and a many times larger output file
+ # should explicitly switch the debug mode on
+ # otherwise we let ld strip the output file
+ # (On my machine: 10KiB < stripped_file < ??100KiB
+ # unstripped_file = stripped_file + XXX KiB
+ # ( XXX=254 for a typical python extension))
+ if not debug:
+ extra_preargs.append("-s")
+
+ super().link(
+ target_desc,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ None, # export_symbols, we do this in our def-file
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def runtime_library_dir_option(self, dir):
+ # cygwin doesn't support rpath. While in theory we could error
+ # out like MSVC does, code might expect it to work like on Unix, so
+ # just warn and hope for the best.
+ self.warn(_runtime_library_dirs_msg)
+ return []
+
+ # -- Miscellaneous methods -----------------------------------------
+
+ def _make_out_path(self, output_dir, strip_dir, src_name):
+ # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+ norm_src_name = os.path.normcase(src_name)
+ return super()._make_out_path(output_dir, strip_dir, norm_src_name)
+
+ @property
+ def out_extensions(self):
+ """
+ Add support for rc and res files.
+ """
+ return {
+ **super().out_extensions,
+ **{ext: ext + self.obj_extension for ext in ('.res', '.rc')},
+ }
+
+
+# the same as cygwin plus some additional parameters
+class MinGW32Compiler(Compiler):
+ """Handles the Mingw32 port of the GNU C compiler to Windows."""
+
+ compiler_type = 'mingw32'
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+
+ shared_option = "-shared"
+
+ if is_cygwincc(self.cc):
+ raise Error('Cygwin gcc cannot be used with --compiler=mingw32')
+
+ self.set_executables(
+ compiler=f'{self.cc} -O -Wall',
+ compiler_so=f'{self.cc} -shared -O -Wall',
+ compiler_so_cxx=f'{self.cxx} -shared -O -Wall',
+ compiler_cxx=f'{self.cxx} -O -Wall',
+ linker_exe=f'{self.cc}',
+ linker_so=f'{self.linker_dll} {shared_option}',
+ linker_exe_cxx=f'{self.cxx}',
+ linker_so_cxx=f'{self.linker_dll_cxx} {shared_option}',
+ )
+
+ def runtime_library_dir_option(self, dir):
+ raise DistutilsPlatformError(_runtime_library_dirs_msg)
+
+
+# Because these compilers aren't configured in Python's pyconfig.h file by
+# default, we should at least warn the user if he is using an unmodified
+# version.
+
+CONFIG_H_OK = "ok"
+CONFIG_H_NOTOK = "not ok"
+CONFIG_H_UNCERTAIN = "uncertain"
+
+
+def check_config_h():
+ """Check if the current Python installation appears amenable to building
+ extensions with GCC.
+
+ Returns a tuple (status, details), where 'status' is one of the following
+ constants:
+
+ - CONFIG_H_OK: all is well, go ahead and compile
+ - CONFIG_H_NOTOK: doesn't look good
+ - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
+
+ 'details' is a human-readable string explaining the situation.
+
+ Note there are two ways to conclude "OK": either 'sys.version' contains
+ the string "GCC" (implying that this Python was built with GCC), or the
+ installed "pyconfig.h" contains the string "__GNUC__".
+ """
+
+ # XXX since this function also checks sys.version, it's not strictly a
+ # "pyconfig.h" check -- should probably be renamed...
+
+ from distutils import sysconfig
+
+ # if sys.version contains GCC then python was compiled with GCC, and the
+ # pyconfig.h file should be OK
+ if "GCC" in sys.version:
+ return CONFIG_H_OK, "sys.version mentions 'GCC'"
+
+ # Clang would also work
+ if "Clang" in sys.version:
+ return CONFIG_H_OK, "sys.version mentions 'Clang'"
+
+ # let's see if __GNUC__ is mentioned in python.h
+ fn = sysconfig.get_config_h_filename()
+ try:
+ config_h = pathlib.Path(fn).read_text(encoding='utf-8')
+ except OSError as exc:
+ return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}")
+ else:
+ substring = '__GNUC__'
+ if substring in config_h:
+ code = CONFIG_H_OK
+ mention_inflected = 'mentions'
+ else:
+ code = CONFIG_H_NOTOK
+ mention_inflected = 'does not mention'
+ return code, f"{fn!r} {mention_inflected} {substring!r}"
+
+
+def is_cygwincc(cc):
+ """Try to determine if the compiler that would be used is from cygwin."""
+ out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
+ return out_string.strip().endswith(b'cygwin')
+
+
+get_versions = None
+"""
+A stand-in for the previous get_versions() function to prevent failures
+when monkeypatched. See pypa/setuptools#2969.
+"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py
new file mode 100644
index 00000000..01328592
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/errors.py
@@ -0,0 +1,24 @@
+class Error(Exception):
+ """Some compile/link operation failed."""
+
+
+class PreprocessError(Error):
+ """Failure to preprocess one or more C/C++ files."""
+
+
+class CompileError(Error):
+ """Failure to compile one or more C/C++ source files."""
+
+
+class LibError(Error):
+ """Failure to create a static library from one or more C/C++ object
+ files."""
+
+
+class LinkError(Error):
+ """Failure to link one or more C/C++ object files into an executable
+ or shared library file."""
+
+
+class UnknownFileType(Error):
+ """Attempt to process an unknown file type."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py
new file mode 100644
index 00000000..6db062a9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/msvc.py
@@ -0,0 +1,614 @@
+"""distutils._msvccompiler
+
+Contains MSVCCompiler, an implementation of the abstract CCompiler class
+for Microsoft Visual Studio 2015.
+
+This module requires VS 2015 or later.
+"""
+
+# 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
+from __future__ import annotations
+
+import contextlib
+import os
+import subprocess
+import unittest.mock as mock
+import warnings
+from collections.abc import Iterable
+
+with contextlib.suppress(ImportError):
+ import winreg
+
+from itertools import count
+
+from ..._log import log
+from ...errors import (
+ DistutilsExecError,
+ DistutilsPlatformError,
+)
+from ...util import get_host_platform, get_platform
+from . import base
+from .base import gen_lib_options
+from .errors import (
+ CompileError,
+ LibError,
+ LinkError,
+)
+
+
+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,
+ )
+ 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
+
+ variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64'
+ suitable_components = (
+ f"Microsoft.VisualStudio.Component.VC.Tools.{variant}",
+ "Microsoft.VisualStudio.Workload.WDExpress",
+ )
+
+ 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()
+ )
+
+ 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 = {
+ '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(
+ '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(
+ 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
+
+
+_vcvars_names = {
+ 'win32': 'x86',
+ '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 Compiler(base.Compiler):
+ """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=False, dry_run=False, force=False) -> None:
+ 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: str | None = None) -> 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 _vcvars_names:
+ raise DistutilsPlatformError(
+ f"--plat-name must be one of {tuple(_vcvars_names)}"
+ )
+
+ 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."
+ )
+ 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 = {
+ (base.Compiler.EXECUTABLE, None): self.ldflags_exe,
+ (base.Compiler.EXECUTABLE, False): self.ldflags_exe,
+ (base.Compiler.EXECUTABLE, True): self.ldflags_exe_debug,
+ (base.Compiler.SHARED_OBJECT, None): self.ldflags_shared,
+ (base.Compiler.SHARED_OBJECT, False): self.ldflags_shared,
+ (base.Compiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
+ (base.Compiler.SHARED_LIBRARY, None): self.ldflags_static,
+ (base.Compiler.SHARED_LIBRARY, False): self.ldflags_static,
+ (base.Compiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
+ }
+
+ self.initialized = True
+
+ # -- Worker methods ------------------------------------------------
+
+ @property
+ def out_extensions(self) -> dict[str, str]:
+ 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=False,
+ 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 = f"/Tc{src}"
+ elif ext in self._cpp_extensions:
+ input_opt = f"/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.extend((input_opt, "/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: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ debug: bool = False,
+ target_lang: str | None = None,
+ ) -> 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: str,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ) -> 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=False):
+ # 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
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py
new file mode 100644
index 00000000..a762e2b6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_base.py
@@ -0,0 +1,83 @@
+import platform
+import sysconfig
+import textwrap
+
+import pytest
+
+from .. import base
+
+pytestmark = pytest.mark.usefixtures('suppress_path_mangle')
+
+
+@pytest.fixture
+def c_file(tmp_path):
+ c_file = tmp_path / 'foo.c'
+ gen_headers = ('Python.h',)
+ is_windows = platform.system() == "Windows"
+ plat_headers = ('windows.h',) * is_windows
+ all_headers = gen_headers + plat_headers
+ headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
+ payload = (
+ textwrap.dedent(
+ """
+ #headers
+ void PyInit_foo(void) {}
+ """
+ )
+ .lstrip()
+ .replace('#headers', headers)
+ )
+ c_file.write_text(payload, encoding='utf-8')
+ return c_file
+
+
+def test_set_include_dirs(c_file):
+ """
+ Extensions should build even if set_include_dirs is invoked.
+ In particular, compiler-specific paths should not be overridden.
+ """
+ compiler = base.new_compiler()
+ python = sysconfig.get_paths()['include']
+ compiler.set_include_dirs([python])
+ compiler.compile([c_file])
+
+ # do it again, setting include dirs after any initialization
+ compiler.set_include_dirs([python])
+ compiler.compile([c_file])
+
+
+def test_has_function_prototype():
+ # Issue https://github.com/pypa/setuptools/issues/3648
+ # Test prototype-generating behavior.
+
+ compiler = base.new_compiler()
+
+ # Every C implementation should have these.
+ assert compiler.has_function('abort')
+ assert compiler.has_function('exit')
+ with pytest.deprecated_call(match='includes is deprecated'):
+ # abort() is a valid expression with the <stdlib.h> prototype.
+ assert compiler.has_function('abort', includes=['stdlib.h'])
+ with pytest.deprecated_call(match='includes is deprecated'):
+ # But exit() is not valid with the actual prototype in scope.
+ assert not compiler.has_function('exit', includes=['stdlib.h'])
+ # And setuptools_does_not_exist is not declared or defined at all.
+ assert not compiler.has_function('setuptools_does_not_exist')
+ with pytest.deprecated_call(match='includes is deprecated'):
+ assert not compiler.has_function(
+ 'setuptools_does_not_exist', includes=['stdio.h']
+ )
+
+
+def test_include_dirs_after_multiple_compile_calls(c_file):
+ """
+ Calling compile multiple times should not change the include dirs
+ (regression test for setuptools issue #3591).
+ """
+ compiler = base.new_compiler()
+ python = sysconfig.get_paths()['include']
+ compiler.set_include_dirs([python])
+ compiler.compile([c_file])
+ assert compiler.include_dirs == [python]
+ compiler.compile([c_file])
+ assert compiler.include_dirs == [python]
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py
new file mode 100644
index 00000000..9adf6b8e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_cygwin.py
@@ -0,0 +1,76 @@
+"""Tests for distutils.cygwinccompiler."""
+
+import os
+import sys
+from distutils import sysconfig
+from distutils.tests import support
+
+import pytest
+
+from .. import cygwin
+
+
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+ self = request.instance
+ self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+ monkeypatch.setattr(sysconfig, 'get_config_h_filename', self._get_config_h_filename)
+ monkeypatch.setattr(sys, 'version', sys.version)
+
+
+class TestCygwinCCompiler(support.TempdirManager):
+ def _get_config_h_filename(self):
+ return self.python_h
+
+ @pytest.mark.skipif('sys.platform != "cygwin"')
+ @pytest.mark.skipif('not os.path.exists("/usr/lib/libbash.dll.a")')
+ def test_find_library_file(self):
+ from distutils.cygwinccompiler import CygwinCCompiler
+
+ compiler = CygwinCCompiler()
+ link_name = "bash"
+ linkable_file = compiler.find_library_file(["/usr/lib"], link_name)
+ assert linkable_file is not None
+ assert os.path.exists(linkable_file)
+ assert linkable_file == f"/usr/lib/lib{link_name:s}.dll.a"
+
+ @pytest.mark.skipif('sys.platform != "cygwin"')
+ def test_runtime_library_dir_option(self):
+ from distutils.cygwinccompiler import CygwinCCompiler
+
+ compiler = CygwinCCompiler()
+ assert compiler.runtime_library_dir_option('/foo') == []
+
+ def test_check_config_h(self):
+ # check_config_h looks for "GCC" in sys.version first
+ # returns CONFIG_H_OK if found
+ sys.version = (
+ '2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC '
+ '4.0.1 (Apple Computer, Inc. build 5370)]'
+ )
+
+ assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
+
+ # then it tries to see if it can find "__GNUC__" in pyconfig.h
+ sys.version = 'something without the *CC word'
+
+ # if the file doesn't exist it returns CONFIG_H_UNCERTAIN
+ assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_UNCERTAIN
+
+ # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
+ self.write_file(self.python_h, 'xxx')
+ assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_NOTOK
+
+ # and CONFIG_H_OK if __GNUC__ is found
+ self.write_file(self.python_h, 'xxx __GNUC__ xxx')
+ assert cygwin.check_config_h()[0] == cygwin.CONFIG_H_OK
+
+ def test_get_msvcr(self):
+ assert cygwin.get_msvcr() == []
+
+ @pytest.mark.skipif('sys.platform != "cygwin"')
+ def test_dll_libraries_not_none(self):
+ from distutils.cygwinccompiler import CygwinCCompiler
+
+ compiler = CygwinCCompiler()
+ assert compiler.dll_libraries is not None
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py
new file mode 100644
index 00000000..dc45687a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_mingw.py
@@ -0,0 +1,48 @@
+from distutils import sysconfig
+from distutils.errors import DistutilsPlatformError
+from distutils.util import is_mingw, split_quoted
+
+import pytest
+
+from .. import cygwin, errors
+
+
+class TestMinGW32Compiler:
+ @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+ def test_compiler_type(self):
+ compiler = cygwin.MinGW32Compiler()
+ assert compiler.compiler_type == 'mingw32'
+
+ @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+ def test_set_executables(self, monkeypatch):
+ monkeypatch.setenv('CC', 'cc')
+ monkeypatch.setenv('CXX', 'c++')
+
+ compiler = cygwin.MinGW32Compiler()
+
+ assert compiler.compiler == split_quoted('cc -O -Wall')
+ assert compiler.compiler_so == split_quoted('cc -shared -O -Wall')
+ assert compiler.compiler_cxx == split_quoted('c++ -O -Wall')
+ assert compiler.linker_exe == split_quoted('cc')
+ assert compiler.linker_so == split_quoted('cc -shared')
+
+ @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+ def test_runtime_library_dir_option(self):
+ compiler = cygwin.MinGW32Compiler()
+ with pytest.raises(DistutilsPlatformError):
+ compiler.runtime_library_dir_option('/usr/lib')
+
+ @pytest.mark.skipif(not is_mingw(), reason='not on mingw')
+ def test_cygwincc_error(self, monkeypatch):
+ monkeypatch.setattr(cygwin, 'is_cygwincc', lambda _: True)
+
+ with pytest.raises(errors.Error):
+ cygwin.MinGW32Compiler()
+
+ @pytest.mark.skipif('sys.platform == "cygwin"')
+ def test_customize_compiler_with_msvc_python(self):
+ # In case we have an MSVC Python build, but still want to use
+ # MinGW32Compiler, then customize_compiler() shouldn't fail at least.
+ # https://github.com/pypa/setuptools/issues/4456
+ compiler = cygwin.MinGW32Compiler()
+ sysconfig.customize_compiler(compiler)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py
new file mode 100644
index 00000000..eca83199
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_msvc.py
@@ -0,0 +1,136 @@
+import os
+import sys
+import sysconfig
+import threading
+import unittest.mock as mock
+from distutils.errors import DistutilsPlatformError
+from distutils.tests import support
+from distutils.util import get_platform
+
+import pytest
+
+from .. import msvc
+
+needs_winreg = pytest.mark.skipif('not hasattr(msvc, "winreg")')
+
+
+class Testmsvccompiler(support.TempdirManager):
+ def test_no_compiler(self, monkeypatch):
+ # makes sure query_vcvarsall raises
+ # a DistutilsPlatformError if the compiler
+ # is not found
+ def _find_vcvarsall(plat_spec):
+ return None, None
+
+ monkeypatch.setattr(msvc, '_find_vcvarsall', _find_vcvarsall)
+
+ with pytest.raises(DistutilsPlatformError):
+ msvc._get_vc_env(
+ 'wont find this version',
+ )
+
+ @pytest.mark.skipif(
+ not sysconfig.get_platform().startswith("win"),
+ reason="Only run test for non-mingw Windows platforms",
+ )
+ @pytest.mark.parametrize(
+ "plat_name, expected",
+ [
+ ("win-arm64", "win-arm64"),
+ ("win-amd64", "win-amd64"),
+ (None, get_platform()),
+ ],
+ )
+ def test_cross_platform_compilation_paths(self, monkeypatch, plat_name, expected):
+ """
+ Ensure a specified target platform is passed to _get_vcvars_spec.
+ """
+ compiler = msvc.Compiler()
+
+ def _get_vcvars_spec(host_platform, platform):
+ assert platform == expected
+
+ monkeypatch.setattr(msvc, '_get_vcvars_spec', _get_vcvars_spec)
+ compiler.initialize(plat_name)
+
+ @needs_winreg
+ def test_get_vc_env_unicode(self):
+ test_var = 'ṰḖṤṪ┅ṼẨṜ'
+ test_value = '₃⁴₅'
+
+ # Ensure we don't early exit from _get_vc_env
+ old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None)
+ os.environ[test_var] = test_value
+ try:
+ env = msvc._get_vc_env('x86')
+ assert test_var.lower() in env
+ assert test_value == env[test_var.lower()]
+ finally:
+ os.environ.pop(test_var)
+ if old_distutils_use_sdk:
+ os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
+
+ @needs_winreg
+ @pytest.mark.parametrize('ver', (2015, 2017))
+ def test_get_vc(self, ver):
+ # This function cannot be mocked, so pass if VC is found
+ # and skip otherwise.
+ lookup = getattr(msvc, f'_find_vc{ver}')
+ expected_version = {2015: 14, 2017: 15}[ver]
+ version, path = lookup()
+ if not version:
+ pytest.skip(f"VS {ver} is not installed")
+ assert version >= expected_version
+ assert os.path.isdir(path)
+
+
+class CheckThread(threading.Thread):
+ exc_info = None
+
+ def run(self):
+ try:
+ super().run()
+ except Exception:
+ self.exc_info = sys.exc_info()
+
+ def __bool__(self):
+ return not self.exc_info
+
+
+class TestSpawn:
+ def test_concurrent_safe(self):
+ """
+ Concurrent calls to spawn should have consistent results.
+ """
+ compiler = msvc.Compiler()
+ compiler._paths = "expected"
+ inner_cmd = 'import os; assert os.environ["PATH"] == "expected"'
+ command = [sys.executable, '-c', inner_cmd]
+
+ threads = [
+ CheckThread(target=compiler.spawn, args=[command]) for n in range(100)
+ ]
+ for thread in threads:
+ thread.start()
+ for thread in threads:
+ thread.join()
+ assert all(threads)
+
+ def test_concurrent_safe_fallback(self):
+ """
+ If CCompiler.spawn has been monkey-patched without support
+ for an env, it should still execute.
+ """
+ from distutils import ccompiler
+
+ compiler = msvc.Compiler()
+ compiler._paths = "expected"
+
+ def CCompiler_spawn(self, cmd):
+ "A spawn without an env argument."
+ assert os.environ["PATH"] == "expected"
+
+ with mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
+ compiler.spawn(["n/a"])
+
+ assert os.environ.get("PATH") != "expected"
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py
new file mode 100644
index 00000000..f4e28984
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/tests/test_unix.py
@@ -0,0 +1,350 @@
+"""Tests for distutils.unixccompiler."""
+
+import os
+import sys
+import unittest.mock as mock
+from distutils import sysconfig
+from distutils.compat import consolidate_linker_args
+from distutils.errors import DistutilsPlatformError
+from distutils.tests import support
+from distutils.tests.compat.py39 import EnvironmentVarGuard
+from distutils.util import _clear_cached_macosx_ver
+
+import pytest
+
+from .. import unix
+
+
+@pytest.fixture(autouse=True)
+def save_values(monkeypatch):
+ monkeypatch.setattr(sys, 'platform', sys.platform)
+ monkeypatch.setattr(sysconfig, 'get_config_var', sysconfig.get_config_var)
+ monkeypatch.setattr(sysconfig, 'get_config_vars', sysconfig.get_config_vars)
+
+
+@pytest.fixture(autouse=True)
+def compiler_wrapper(request):
+ class CompilerWrapper(unix.Compiler):
+ def rpath_foo(self):
+ return self.runtime_library_dir_option('/foo')
+
+ request.instance.cc = CompilerWrapper()
+
+
+class TestUnixCCompiler(support.TempdirManager):
+ @pytest.mark.skipif('platform.system == "Windows"')
+ def test_runtime_libdir_option(self): # noqa: C901
+ # Issue #5900; GitHub Issue #37
+ #
+ # Ensure RUNPATH is added to extension modules with RPATH if
+ # GNU ld is used
+
+ # darwin
+ sys.platform = 'darwin'
+ darwin_ver_var = 'MACOSX_DEPLOYMENT_TARGET'
+ darwin_rpath_flag = '-Wl,-rpath,/foo'
+ darwin_lib_flag = '-L/foo'
+
+ # (macOS version from syscfg, macOS version from env var) -> flag
+ # Version value of None generates two tests: as None and as empty string
+ # Expected flag value of None means an mismatch exception is expected
+ darwin_test_cases = [
+ ((None, None), darwin_lib_flag),
+ ((None, '11'), darwin_rpath_flag),
+ (('10', None), darwin_lib_flag),
+ (('10.3', None), darwin_lib_flag),
+ (('10.3.1', None), darwin_lib_flag),
+ (('10.5', None), darwin_rpath_flag),
+ (('10.5.1', None), darwin_rpath_flag),
+ (('10.3', '10.3'), darwin_lib_flag),
+ (('10.3', '10.5'), darwin_rpath_flag),
+ (('10.5', '10.3'), darwin_lib_flag),
+ (('10.5', '11'), darwin_rpath_flag),
+ (('10.4', '10'), None),
+ ]
+
+ def make_darwin_gcv(syscfg_macosx_ver):
+ def gcv(var):
+ if var == darwin_ver_var:
+ return syscfg_macosx_ver
+ return "xxx"
+
+ return gcv
+
+ def do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag):
+ env = os.environ
+ msg = f"macOS version = (sysconfig={syscfg_macosx_ver!r}, env={env_macosx_ver!r})"
+
+ # Save
+ old_gcv = sysconfig.get_config_var
+ old_env_macosx_ver = env.get(darwin_ver_var)
+
+ # Setup environment
+ _clear_cached_macosx_ver()
+ sysconfig.get_config_var = make_darwin_gcv(syscfg_macosx_ver)
+ if env_macosx_ver is not None:
+ env[darwin_ver_var] = env_macosx_ver
+ elif darwin_ver_var in env:
+ env.pop(darwin_ver_var)
+
+ # Run the test
+ if expected_flag is not None:
+ assert self.cc.rpath_foo() == expected_flag, msg
+ else:
+ with pytest.raises(
+ DistutilsPlatformError, match=darwin_ver_var + r' mismatch'
+ ):
+ self.cc.rpath_foo()
+
+ # Restore
+ if old_env_macosx_ver is not None:
+ env[darwin_ver_var] = old_env_macosx_ver
+ elif darwin_ver_var in env:
+ env.pop(darwin_ver_var)
+ sysconfig.get_config_var = old_gcv
+ _clear_cached_macosx_ver()
+
+ for macosx_vers, expected_flag in darwin_test_cases:
+ syscfg_macosx_ver, env_macosx_ver = macosx_vers
+ do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag)
+ # Bonus test cases with None interpreted as empty string
+ if syscfg_macosx_ver is None:
+ do_darwin_test("", env_macosx_ver, expected_flag)
+ if env_macosx_ver is None:
+ do_darwin_test(syscfg_macosx_ver, "", expected_flag)
+ if syscfg_macosx_ver is None and env_macosx_ver is None:
+ do_darwin_test("", "", expected_flag)
+
+ old_gcv = sysconfig.get_config_var
+
+ # hp-ux
+ sys.platform = 'hp-ux'
+
+ def gcv(v):
+ return 'xxx'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == ['+s', '-L/foo']
+
+ def gcv(v):
+ return 'gcc'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
+
+ def gcv(v):
+ return 'g++'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
+
+ sysconfig.get_config_var = old_gcv
+
+ # GCC GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'gcc'
+ elif v == 'GNULD':
+ return 'yes'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == consolidate_linker_args([
+ '-Wl,--enable-new-dtags',
+ '-Wl,-rpath,/foo',
+ ])
+
+ def gcv(v):
+ if v == 'CC':
+ return 'gcc -pthread -B /bar'
+ elif v == 'GNULD':
+ return 'yes'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == consolidate_linker_args([
+ '-Wl,--enable-new-dtags',
+ '-Wl,-rpath,/foo',
+ ])
+
+ # GCC non-GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'gcc'
+ elif v == 'GNULD':
+ return 'no'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == '-Wl,-R/foo'
+
+ # GCC GNULD with fully qualified configuration prefix
+ # see #7617
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'x86_64-pc-linux-gnu-gcc-4.4.2'
+ elif v == 'GNULD':
+ return 'yes'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == consolidate_linker_args([
+ '-Wl,--enable-new-dtags',
+ '-Wl,-rpath,/foo',
+ ])
+
+ # non-GCC GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'cc'
+ elif v == 'GNULD':
+ return 'yes'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == consolidate_linker_args([
+ '-Wl,--enable-new-dtags',
+ '-Wl,-rpath,/foo',
+ ])
+
+ # non-GCC non-GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'cc'
+ elif v == 'GNULD':
+ return 'no'
+
+ sysconfig.get_config_var = gcv
+ assert self.cc.rpath_foo() == '-Wl,-R/foo'
+
+ @pytest.mark.skipif('platform.system == "Windows"')
+ def test_cc_overrides_ldshared(self):
+ # Issue #18080:
+ # ensure that setting CC env variable also changes default linker
+ def gcv(v):
+ if v == 'LDSHARED':
+ return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+ return 'gcc-4.2'
+
+ def gcvs(*args, _orig=sysconfig.get_config_vars):
+ if args:
+ return list(map(sysconfig.get_config_var, args))
+ return _orig()
+
+ sysconfig.get_config_var = gcv
+ sysconfig.get_config_vars = gcvs
+ with EnvironmentVarGuard() as env:
+ env['CC'] = 'my_cc'
+ del env['LDSHARED']
+ sysconfig.customize_compiler(self.cc)
+ assert self.cc.linker_so[0] == 'my_cc'
+
+ @pytest.mark.skipif('platform.system == "Windows"')
+ @pytest.mark.usefixtures('disable_macos_customization')
+ def test_cc_overrides_ldshared_for_cxx_correctly(self):
+ """
+ Ensure that setting CC env variable also changes default linker
+ correctly when building C++ extensions.
+
+ pypa/distutils#126
+ """
+
+ def gcv(v):
+ if v == 'LDSHARED':
+ return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+ elif v == 'LDCXXSHARED':
+ return 'g++-4.2 -bundle -undefined dynamic_lookup '
+ elif v == 'CXX':
+ return 'g++-4.2'
+ elif v == 'CC':
+ return 'gcc-4.2'
+ return ''
+
+ def gcvs(*args, _orig=sysconfig.get_config_vars):
+ if args:
+ return list(map(sysconfig.get_config_var, args))
+ return _orig()
+
+ sysconfig.get_config_var = gcv
+ sysconfig.get_config_vars = gcvs
+ with (
+ mock.patch.object(self.cc, 'spawn', return_value=None) as mock_spawn,
+ mock.patch.object(self.cc, '_need_link', return_value=True),
+ mock.patch.object(self.cc, 'mkpath', return_value=None),
+ EnvironmentVarGuard() as env,
+ ):
+ env['CC'] = 'ccache my_cc'
+ env['CXX'] = 'my_cxx'
+ del env['LDSHARED']
+ sysconfig.customize_compiler(self.cc)
+ assert self.cc.linker_so[0:2] == ['ccache', 'my_cc']
+ self.cc.link(None, [], 'a.out', target_lang='c++')
+ call_args = mock_spawn.call_args[0][0]
+ expected = ['my_cxx', '-bundle', '-undefined', 'dynamic_lookup']
+ assert call_args[:4] == expected
+
+ @pytest.mark.skipif('platform.system == "Windows"')
+ def test_explicit_ldshared(self):
+ # Issue #18080:
+ # ensure that setting CC env variable does not change
+ # explicit LDSHARED setting for linker
+ def gcv(v):
+ if v == 'LDSHARED':
+ return 'gcc-4.2 -bundle -undefined dynamic_lookup '
+ return 'gcc-4.2'
+
+ def gcvs(*args, _orig=sysconfig.get_config_vars):
+ if args:
+ return list(map(sysconfig.get_config_var, args))
+ return _orig()
+
+ sysconfig.get_config_var = gcv
+ sysconfig.get_config_vars = gcvs
+ with EnvironmentVarGuard() as env:
+ env['CC'] = 'my_cc'
+ env['LDSHARED'] = 'my_ld -bundle -dynamic'
+ sysconfig.customize_compiler(self.cc)
+ assert self.cc.linker_so[0] == 'my_ld'
+
+ def test_has_function(self):
+ # Issue https://github.com/pypa/distutils/issues/64:
+ # ensure that setting output_dir does not raise
+ # FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
+ self.cc.output_dir = 'scratch'
+ os.chdir(self.mkdtemp())
+ self.cc.has_function('abort')
+
+ def test_find_library_file(self, monkeypatch):
+ compiler = unix.Compiler()
+ compiler._library_root = lambda dir: dir
+ monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d)
+
+ libname = 'libabc.dylib' if sys.platform != 'cygwin' else 'cygabc.dll'
+ dirs = ('/foo/bar/missing', '/foo/bar/existing')
+ assert (
+ compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+ == f'/foo/bar/existing/{libname}'
+ )
+ assert (
+ compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+ == f'/foo/bar/existing/{libname}'
+ )
+
+ monkeypatch.setattr(
+ os.path,
+ 'exists',
+ lambda d: 'existing' in d and '.a' in d and '.dll.a' not in d,
+ )
+ assert (
+ compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+ == '/foo/bar/existing/libabc.a'
+ )
+ assert (
+ compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+ == '/foo/bar/existing/libabc.a'
+ )
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py
new file mode 100644
index 00000000..e8a53d45
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/unix.py
@@ -0,0 +1,423 @@
+"""distutils.unixccompiler
+
+Contains the UnixCCompiler class, a subclass of CCompiler that handles
+the "typical" Unix-style command-line C compiler:
+ * macros defined with -Dname[=value]
+ * macros undefined with -Uname
+ * include search directories specified with -Idir
+ * libraries specified with -lllib
+ * library search directories specified with -Ldir
+ * compile handled by 'cc' (or similar) executable with -c option:
+ compiles .c to .o
+ * link static library handled by 'ar' command (possibly with 'ranlib')
+ * link shared library handled by 'cc -shared'
+"""
+
+from __future__ import annotations
+
+import itertools
+import os
+import re
+import shlex
+import sys
+from collections.abc import Iterable
+
+from ... import sysconfig
+from ..._log import log
+from ..._macos_compat import compiler_fixup
+from ..._modified import newer
+from ...compat import consolidate_linker_args
+from ...errors import DistutilsExecError
+from . import base
+from .base import _Macro, gen_lib_options, gen_preprocess_options
+from .errors import (
+ CompileError,
+ LibError,
+ LinkError,
+)
+
+# XXX Things not currently handled:
+# * optimization/debug/warning flags; we just use whatever's in Python's
+# Makefile and live with it. Is this adequate? If not, we might
+# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
+# SunCCompiler, and I suspect down that road lies madness.
+# * even if we don't know a warning flag from an optimization flag,
+# we need some way for outsiders to feed preprocessor/compiler/linker
+# flags in to us -- eg. a sysadmin might want to mandate certain flags
+# via a site config file, or a user might want to set something for
+# compiling this module distribution only via the setup.py command
+# line, whatever. As long as these options come from something on the
+# current system, they can be as system-dependent as they like, and we
+# should just happily stuff them into the preprocessor/compiler/linker
+# options and carry on.
+
+
+def _split_env(cmd):
+ """
+ For macOS, split command into 'env' portion (if any)
+ and the rest of the linker command.
+
+ >>> _split_env(['a', 'b', 'c'])
+ ([], ['a', 'b', 'c'])
+ >>> _split_env(['/usr/bin/env', 'A=3', 'gcc'])
+ (['/usr/bin/env', 'A=3'], ['gcc'])
+ """
+ pivot = 0
+ if os.path.basename(cmd[0]) == "env":
+ pivot = 1
+ while '=' in cmd[pivot]:
+ pivot += 1
+ return cmd[:pivot], cmd[pivot:]
+
+
+def _split_aix(cmd):
+ """
+ AIX platforms prefix the compiler with the ld_so_aix
+ script, so split that from the linker command.
+
+ >>> _split_aix(['a', 'b', 'c'])
+ ([], ['a', 'b', 'c'])
+ >>> _split_aix(['/bin/foo/ld_so_aix', 'gcc'])
+ (['/bin/foo/ld_so_aix'], ['gcc'])
+ """
+ pivot = os.path.basename(cmd[0]) == 'ld_so_aix'
+ return cmd[:pivot], cmd[pivot:]
+
+
+def _linker_params(linker_cmd, compiler_cmd):
+ """
+ The linker command usually begins with the compiler
+ command (possibly multiple elements), followed by zero or more
+ params for shared library building.
+
+ If the LDSHARED env variable overrides the linker command,
+ however, the commands may not match.
+
+ Return the best guess of the linker parameters by stripping
+ the linker command. If the compiler command does not
+ match the linker command, assume the linker command is
+ just the first element.
+
+ >>> _linker_params('gcc foo bar'.split(), ['gcc'])
+ ['foo', 'bar']
+ >>> _linker_params('gcc foo bar'.split(), ['other'])
+ ['foo', 'bar']
+ >>> _linker_params('ccache gcc foo bar'.split(), 'ccache gcc'.split())
+ ['foo', 'bar']
+ >>> _linker_params(['gcc'], ['gcc'])
+ []
+ """
+ c_len = len(compiler_cmd)
+ pivot = c_len if linker_cmd[:c_len] == compiler_cmd else 1
+ return linker_cmd[pivot:]
+
+
+class Compiler(base.Compiler):
+ compiler_type = 'unix'
+
+ # These are used by CCompiler in two places: the constructor sets
+ # instance attributes 'preprocessor', 'compiler', etc. from them, and
+ # 'set_executable()' allows any of these to be set. The defaults here
+ # are pretty generic; they will probably have to be set by an outsider
+ # (eg. using information discovered by the sysconfig about building
+ # Python extensions).
+ executables = {
+ 'preprocessor': None,
+ 'compiler': ["cc"],
+ 'compiler_so': ["cc"],
+ 'compiler_cxx': ["c++"],
+ 'compiler_so_cxx': ["c++"],
+ 'linker_so': ["cc", "-shared"],
+ 'linker_so_cxx': ["c++", "-shared"],
+ 'linker_exe': ["cc"],
+ 'linker_exe_cxx': ["c++", "-shared"],
+ 'archiver': ["ar", "-cr"],
+ 'ranlib': None,
+ }
+
+ if sys.platform[:6] == "darwin":
+ executables['ranlib'] = ["ranlib"]
+
+ # Needed for the filename generation methods provided by the base
+ # class, CCompiler. NB. whoever instantiates/uses a particular
+ # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
+ # reasonable common default here, but it's not necessarily used on all
+ # Unices!
+
+ src_extensions = [".c", ".C", ".cc", ".cxx", ".cpp", ".m"]
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".so"
+ dylib_lib_extension = ".dylib"
+ xcode_stub_lib_extension = ".tbd"
+ static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
+ xcode_stub_lib_format = dylib_lib_format
+ if sys.platform == "cygwin":
+ exe_extension = ".exe"
+ shared_lib_extension = ".dll.a"
+ dylib_lib_extension = ".dll"
+ dylib_lib_format = "cyg%s%s"
+
+ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+ """Remove standard library path from rpath"""
+ libraries, library_dirs, runtime_library_dirs = super()._fix_lib_args(
+ libraries, library_dirs, runtime_library_dirs
+ )
+ libdir = sysconfig.get_config_var('LIBDIR')
+ if (
+ runtime_library_dirs
+ and libdir.startswith("/usr/lib")
+ and (libdir in runtime_library_dirs)
+ ):
+ runtime_library_dirs.remove(libdir)
+ return libraries, library_dirs, runtime_library_dirs
+
+ def preprocess(
+ self,
+ source: str | os.PathLike[str],
+ output_file: str | os.PathLike[str] | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ ):
+ fixed_args = self._fix_compile_args(None, macros, include_dirs)
+ ignore, macros, include_dirs = fixed_args
+ pp_opts = gen_preprocess_options(macros, include_dirs)
+ pp_args = self.preprocessor + pp_opts
+ if output_file:
+ pp_args.extend(['-o', output_file])
+ if extra_preargs:
+ pp_args[:0] = extra_preargs
+ if extra_postargs:
+ pp_args.extend(extra_postargs)
+ pp_args.append(source)
+
+ # reasons to preprocess:
+ # - force is indicated
+ # - output is directed to stdout
+ # - source file is newer than the target
+ preprocess = self.force or output_file is None or newer(source, output_file)
+ if not preprocess:
+ return
+
+ if output_file:
+ self.mkpath(os.path.dirname(output_file))
+
+ try:
+ self.spawn(pp_args)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs)
+ compiler_so_cxx = compiler_fixup(self.compiler_so_cxx, cc_args + extra_postargs)
+ try:
+ if self.detect_language(src) == 'c++':
+ self.spawn(
+ compiler_so_cxx + cc_args + [src, '-o', obj] + extra_postargs
+ )
+ else:
+ self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def create_static_lib(
+ self, objects, output_libname, output_dir=None, debug=False, 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):
+ self.mkpath(os.path.dirname(output_filename))
+ self.spawn(self.archiver + [output_filename] + objects + self.objects)
+
+ # Not many Unices required ranlib anymore -- SunOS 4.x is, I
+ # think the only major Unix that does. Maybe we need some
+ # platform intelligence here to skip ranlib if it's not
+ # needed -- or maybe Python's configure script took care of
+ # it for us, hence the check for leading colon.
+ if self.ranlib:
+ try:
+ self.spawn(self.ranlib + [output_filename])
+ except DistutilsExecError as msg:
+ raise LibError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ def link(
+ self,
+ target_desc,
+ objects: list[str] | tuple[str, ...],
+ output_filename,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ 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
+
+ lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+ if not isinstance(output_dir, (str, type(None))):
+ raise TypeError("'output_dir' must be a string or None")
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
+ if debug:
+ ld_args[:0] = ['-g']
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ # Select a linker based on context: linker_exe when
+ # building an executable or linker_so (with shared options)
+ # when building a shared library.
+ building_exe = target_desc == base.Compiler.EXECUTABLE
+ linker = (
+ self.linker_exe
+ if building_exe
+ else (
+ self.linker_so_cxx if target_lang == "c++" else self.linker_so
+ )
+ )[:]
+
+ if target_lang == "c++" and self.compiler_cxx:
+ env, linker_ne = _split_env(linker)
+ aix, linker_na = _split_aix(linker_ne)
+ _, compiler_cxx_ne = _split_env(self.compiler_cxx)
+ _, linker_exe_ne = _split_env(self.linker_exe)
+
+ params = _linker_params(linker_na, linker_exe_ne)
+ linker = env + aix + compiler_cxx_ne + params
+
+ linker = compiler_fixup(linker, ld_args)
+
+ self.spawn(linker + ld_args)
+ except DistutilsExecError as msg:
+ raise LinkError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "-L" + dir
+
+ def _is_gcc(self):
+ cc_var = sysconfig.get_config_var("CC")
+ compiler = os.path.basename(shlex.split(cc_var)[0])
+ return "gcc" in compiler or "g++" in compiler
+
+ def runtime_library_dir_option(self, dir: str) -> str | list[str]: # type: ignore[override] # Fixed in pypa/distutils#339
+ # XXX Hackish, at the very least. See Python bug #445902:
+ # https://bugs.python.org/issue445902
+ # Linkers on different platforms need different options to
+ # specify that directories need to be added to the list of
+ # directories searched for dependencies when a dynamic library
+ # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to
+ # be told to pass the -R option through to the linker, whereas
+ # other compilers and gcc on other systems just know this.
+ # Other compilers may need something slightly different. At
+ # this time, there's no way to determine this information from
+ # the configuration data stored in the Python installation, so
+ # we use this hack.
+ if sys.platform[:6] == "darwin":
+ from distutils.util import get_macosx_target_ver, split_version
+
+ macosx_target_ver = get_macosx_target_ver()
+ if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
+ return "-Wl,-rpath," + dir
+ else: # no support for -rpath on earlier macOS versions
+ return "-L" + dir
+ elif sys.platform[:7] == "freebsd":
+ return "-Wl,-rpath=" + dir
+ elif sys.platform[:5] == "hp-ux":
+ return [
+ "-Wl,+s" if self._is_gcc() else "+s",
+ "-L" + dir,
+ ]
+
+ # For all compilers, `-Wl` is the presumed way to pass a
+ # compiler option to the linker
+ if sysconfig.get_config_var("GNULD") == "yes":
+ return consolidate_linker_args([
+ # Force RUNPATH instead of RPATH
+ "-Wl,--enable-new-dtags",
+ "-Wl,-rpath," + dir,
+ ])
+ else:
+ return "-Wl,-R" + dir
+
+ def library_option(self, lib):
+ return "-l" + lib
+
+ @staticmethod
+ def _library_root(dir):
+ """
+ macOS users can specify an alternate SDK using'-isysroot'.
+ Calculate the SDK root if it is specified.
+
+ Note that, as of Xcode 7, Apple SDKs may contain textual stub
+ libraries with .tbd extensions rather than the normal .dylib
+ shared libraries installed in /. The Apple compiler tool
+ chain handles this transparently but it can cause problems
+ for programs that are being built with an SDK and searching
+ for specific libraries. Callers of find_library_file need to
+ keep in mind that the base filename of the returned SDK library
+ file might have a different extension from that of the library
+ file installed on the running system, for example:
+ /Applications/Xcode.app/Contents/Developer/Platforms/
+ MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
+ usr/lib/libedit.tbd
+ vs
+ /usr/lib/libedit.dylib
+ """
+ cflags = sysconfig.get_config_var('CFLAGS')
+ match = re.search(r'-isysroot\s*(\S+)', cflags)
+
+ apply_root = (
+ sys.platform == 'darwin'
+ and match
+ and (
+ dir.startswith('/System/')
+ or (dir.startswith('/usr/') and not dir.startswith('/usr/local/'))
+ )
+ )
+
+ return os.path.join(match.group(1), dir[1:]) if apply_root else dir
+
+ def find_library_file(self, dirs, lib, debug=False):
+ """
+ Second-guess the linker with not much hard
+ data to go on: GCC seems to prefer the shared library, so
+ assume that *all* Unix C compilers do,
+ ignoring even GCC's "-static" option.
+ """
+ lib_names = (
+ self.library_filename(lib, lib_type=type)
+ for type in 'dylib xcode_stub shared static'.split()
+ )
+
+ roots = map(self._library_root, dirs)
+
+ searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names))
+
+ found = filter(os.path.exists, searched)
+
+ # Return None if it could not be found in any dir.
+ return next(found, None)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py
new file mode 100644
index 00000000..82d017fc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_distutils/compilers/C/zos.py
@@ -0,0 +1,230 @@
+"""distutils.zosccompiler
+
+Contains the selection of the c & c++ compilers on z/OS. There are several
+different c compilers on z/OS, all of them are optional, so the correct
+one needs to be chosen based on the users input. This is compatible with
+the following compilers:
+
+IBM C/C++ For Open Enterprise Languages on z/OS 2.0
+IBM Open XL C/C++ 1.1 for z/OS
+IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5
+IBM z/OS XL C/C++
+"""
+
+import os
+
+from ... import sysconfig
+from ...errors import DistutilsExecError
+from . import unix
+from .errors import CompileError
+
+_cc_args = {
+ 'ibm-openxl': [
+ '-m64',
+ '-fvisibility=default',
+ '-fzos-le-char-mode=ascii',
+ '-fno-short-enums',
+ ],
+ 'ibm-xlclang': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ ],
+ 'ibm-xlc': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ '-qlanglvl=extc99',
+ ],
+}
+
+_cxx_args = {
+ 'ibm-openxl': [
+ '-m64',
+ '-fvisibility=default',
+ '-fzos-le-char-mode=ascii',
+ '-fno-short-enums',
+ ],
+ 'ibm-xlclang': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ ],
+ 'ibm-xlc': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ '-qlanglvl=extended0x',
+ ],
+}
+
+_asm_args = {
+ 'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'],
+ 'ibm-xlclang': [],
+ 'ibm-xlc': [],
+}
+
+_ld_args = {
+ 'ibm-openxl': [],
+ 'ibm-xlclang': ['-Wl,dll', '-q64'],
+ 'ibm-xlc': ['-Wl,dll', '-q64'],
+}
+
+
+# Python on z/OS is built with no compiler specific options in it's CFLAGS.
+# But each compiler requires it's own specific options to build successfully,
+# though some of the options are common between them
+class Compiler(unix.Compiler):
+ src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx', '.C']
+ _asm_extensions = ['.s']
+
+ def _get_zos_compiler_name(self):
+ zos_compiler_names = [
+ os.path.basename(binary)
+ for envvar in ('CC', 'CXX', 'LDSHARED')
+ if (binary := os.environ.get(envvar, None))
+ ]
+ if len(zos_compiler_names) == 0:
+ return 'ibm-openxl'
+
+ zos_compilers = {}
+ for compiler in (
+ 'ibm-clang',
+ 'ibm-clang64',
+ 'ibm-clang++',
+ 'ibm-clang++64',
+ 'clang',
+ 'clang++',
+ 'clang-14',
+ ):
+ zos_compilers[compiler] = 'ibm-openxl'
+
+ for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'):
+ zos_compilers[compiler] = 'ibm-xlclang'
+
+ for compiler in ('xlc', 'xlC', 'xlc++'):
+ zos_compilers[compiler] = 'ibm-xlc'
+
+ return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl')
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+ self.zos_compiler = self._get_zos_compiler_name()
+ sysconfig.customize_compiler(self)
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ local_args = []
+ if ext in self._cpp_extensions:
+ compiler = self.compiler_cxx
+ local_args.extend(_cxx_args[self.zos_compiler])
+ elif ext in self._asm_extensions:
+ compiler = self.compiler_so
+ local_args.extend(_cc_args[self.zos_compiler])
+ local_args.extend(_asm_args[self.zos_compiler])
+ else:
+ compiler = self.compiler_so
+ local_args.extend(_cc_args[self.zos_compiler])
+ local_args.extend(cc_args)
+
+ try:
+ self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def runtime_library_dir_option(self, dir):
+ return '-L' + dir
+
+ def link(
+ self,
+ target_desc,
+ objects,
+ output_filename,
+ output_dir=None,
+ libraries=None,
+ library_dirs=None,
+ runtime_library_dirs=None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ # For a built module to use functions from cpython, it needs to use Pythons
+ # side deck file. The side deck is located beside the libpython3.xx.so
+ ldversion = sysconfig.get_config_var('LDVERSION')
+ if sysconfig.python_build:
+ side_deck_path = os.path.join(
+ sysconfig.get_config_var('abs_builddir'),
+ f'libpython{ldversion}.x',
+ )
+ else:
+ side_deck_path = os.path.join(
+ sysconfig.get_config_var('installed_base'),
+ sysconfig.get_config_var('platlibdir'),
+ f'libpython{ldversion}.x',
+ )
+
+ if os.path.exists(side_deck_path):
+ if extra_postargs:
+ extra_postargs.append(side_deck_path)
+ else:
+ extra_postargs = [side_deck_path]
+
+ # Check and replace libraries included side deck files
+ if runtime_library_dirs:
+ for dir in runtime_library_dirs:
+ for library in libraries[:]:
+ library_side_deck = os.path.join(dir, f'{library}.x')
+ if os.path.exists(library_side_deck):
+ libraries.remove(library)
+ extra_postargs.append(library_side_deck)
+ break
+
+ # Any required ld args for the given compiler
+ extra_postargs.extend(_ld_args[self.zos_compiler])
+
+ super().link(
+ target_desc,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )