python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
sysconfig.py
       1  """Provide access to Python's configuration information.  The specific
       2  configuration variables available depend heavily on the platform and
       3  configuration.  The values may be retrieved using
       4  get_config_var(name), and the list of variables is available via
       5  get_config_vars().keys().  Additional convenience functions are also
       6  available.
       7  
       8  Written by:   Fred L. Drake, Jr.
       9  Email:        <fdrake@acm.org>
      10  """
      11  
      12  import os
      13  import re
      14  import sys
      15  import sysconfig
      16  import pathlib
      17  
      18  from .errors import DistutilsPlatformError
      19  from . import py39compat
      20  from ._functools import pass_none
      21  
      22  IS_PYPY = '__pypy__' in sys.builtin_module_names
      23  
      24  # These are needed in a couple of spots, so just compute them once.
      25  PREFIX = os.path.normpath(sys.prefix)
      26  EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
      27  BASE_PREFIX = os.path.normpath(sys.base_prefix)
      28  BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
      29  
      30  # Path to the base directory of the project. On Windows the binary may
      31  # live in project/PCbuild/win32 or project/PCbuild/amd64.
      32  # set for cross builds
      33  if "_PYTHON_PROJECT_BASE" in os.environ:
      34      project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
      35  else:
      36      if sys.executable:
      37          project_base = os.path.dirname(os.path.abspath(sys.executable))
      38      else:
      39          # sys.executable can be empty if argv[0] has been changed and Python is
      40          # unable to retrieve the real program name
      41          project_base = os.getcwd()
      42  
      43  
      44  def _is_python_source_dir(d):
      45      """
      46      Return True if the target directory appears to point to an
      47      un-installed Python.
      48      """
      49      modules = pathlib.Path(d).joinpath('Modules')
      50      return any(modules.joinpath(fn).is_file() for fn in ('Setup', 'Setup.local'))
      51  
      52  
      53  _sys_home = getattr(sys, '_home', None)
      54  
      55  
      56  def _is_parent(dir_a, dir_b):
      57      """
      58      Return True if a is a parent of b.
      59      """
      60      return os.path.normcase(dir_a).startswith(os.path.normcase(dir_b))
      61  
      62  
      63  if os.name == 'nt':
      64  
      65      @pass_none
      66      def _fix_pcbuild(d):
      67          # In a venv, sys._home will be inside BASE_PREFIX rather than PREFIX.
      68          prefixes = PREFIX, BASE_PREFIX
      69          matched = (
      70              prefix
      71              for prefix in prefixes
      72              if _is_parent(d, os.path.join(prefix, "PCbuild"))
      73          )
      74          return next(matched, d)
      75  
      76      project_base = _fix_pcbuild(project_base)
      77      _sys_home = _fix_pcbuild(_sys_home)
      78  
      79  
      80  def _python_build():
      81      if _sys_home:
      82          return _is_python_source_dir(_sys_home)
      83      return _is_python_source_dir(project_base)
      84  
      85  
      86  python_build = _python_build()
      87  
      88  
      89  # Calculate the build qualifier flags if they are defined.  Adding the flags
      90  # to the include and lib directories only makes sense for an installation, not
      91  # an in-source build.
      92  build_flags = ''
      93  try:
      94      if not python_build:
      95          build_flags = sys.abiflags
      96  except AttributeError:
      97      # It's not a configure-based build, so the sys module doesn't have
      98      # this attribute, which is fine.
      99      pass
     100  
     101  
     102  def get_python_version():
     103      """Return a string containing the major and minor Python version,
     104      leaving off the patchlevel.  Sample return values could be '1.5'
     105      or '2.2'.
     106      """
     107      return '%d.%d' % sys.version_info[:2]
     108  
     109  
     110  def get_python_inc(plat_specific=0, prefix=None):
     111      """Return the directory containing installed Python header files.
     112  
     113      If 'plat_specific' is false (the default), this is the path to the
     114      non-platform-specific header files, i.e. Python.h and so on;
     115      otherwise, this is the path to platform-specific header files
     116      (namely pyconfig.h).
     117  
     118      If 'prefix' is supplied, use it instead of sys.base_prefix or
     119      sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
     120      """
     121      default_prefix = BASE_EXEC_PREFIX if plat_specific else BASE_PREFIX
     122      resolved_prefix = prefix if prefix is not None else default_prefix
     123      try:
     124          getter = globals()[f'_get_python_inc_{os.name}']
     125      except KeyError:
     126          raise DistutilsPlatformError(
     127              "I don't know where Python installs its C header files "
     128              "on platform '%s'" % os.name
     129          )
     130      return getter(resolved_prefix, prefix, plat_specific)
     131  
     132  
     133  def _get_python_inc_posix(prefix, spec_prefix, plat_specific):
     134      if IS_PYPY and sys.version_info < (3, 8):
     135          return os.path.join(prefix, 'include')
     136      return (
     137          _get_python_inc_posix_python(plat_specific)
     138          or _get_python_inc_from_config(plat_specific, spec_prefix)
     139          or _get_python_inc_posix_prefix(prefix)
     140      )
     141  
     142  
     143  def _get_python_inc_posix_python(plat_specific):
     144      """
     145      Assume the executable is in the build directory. The
     146      pyconfig.h file should be in the same directory. Since
     147      the build directory may not be the source directory,
     148      use "srcdir" from the makefile to find the "Include"
     149      directory.
     150      """
     151      if not python_build:
     152          return
     153      if plat_specific:
     154          return _sys_home or project_base
     155      incdir = os.path.join(get_config_var('srcdir'), 'Include')
     156      return os.path.normpath(incdir)
     157  
     158  
     159  def _get_python_inc_from_config(plat_specific, spec_prefix):
     160      """
     161      If no prefix was explicitly specified, provide the include
     162      directory from the config vars. Useful when
     163      cross-compiling, since the config vars may come from
     164      the host
     165      platform Python installation, while the current Python
     166      executable is from the build platform installation.
     167  
     168      >>> monkeypatch = getfixture('monkeypatch')
     169      >>> gpifc = _get_python_inc_from_config
     170      >>> monkeypatch.setitem(gpifc.__globals__, 'get_config_var', str.lower)
     171      >>> gpifc(False, '/usr/bin/')
     172      >>> gpifc(False, '')
     173      >>> gpifc(False, None)
     174      'includepy'
     175      >>> gpifc(True, None)
     176      'confincludepy'
     177      """
     178      if spec_prefix is None:
     179          return get_config_var('CONF' * plat_specific + 'INCLUDEPY')
     180  
     181  
     182  def _get_python_inc_posix_prefix(prefix):
     183      implementation = 'pypy' if IS_PYPY else 'python'
     184      python_dir = implementation + get_python_version() + build_flags
     185      return os.path.join(prefix, "include", python_dir)
     186  
     187  
     188  def _get_python_inc_nt(prefix, spec_prefix, plat_specific):
     189      if python_build:
     190          # Include both the include and PC dir to ensure we can find
     191          # pyconfig.h
     192          return (
     193              os.path.join(prefix, "include")
     194              + os.path.pathsep
     195              + os.path.join(prefix, "PC")
     196          )
     197      return os.path.join(prefix, "include")
     198  
     199  
     200  # allow this behavior to be monkey-patched. Ref pypa/distutils#2.
     201  def _posix_lib(standard_lib, libpython, early_prefix, prefix):
     202      if standard_lib:
     203          return libpython
     204      else:
     205          return os.path.join(libpython, "site-packages")
     206  
     207  
     208  def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
     209      """Return the directory containing the Python library (standard or
     210      site additions).
     211  
     212      If 'plat_specific' is true, return the directory containing
     213      platform-specific modules, i.e. any module from a non-pure-Python
     214      module distribution; otherwise, return the platform-shared library
     215      directory.  If 'standard_lib' is true, return the directory
     216      containing standard Python library modules; otherwise, return the
     217      directory for site-specific modules.
     218  
     219      If 'prefix' is supplied, use it instead of sys.base_prefix or
     220      sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
     221      """
     222  
     223      if IS_PYPY and sys.version_info < (3, 8):
     224          # PyPy-specific schema
     225          if prefix is None:
     226              prefix = PREFIX
     227          if standard_lib:
     228              return os.path.join(prefix, "lib-python", sys.version[0])
     229          return os.path.join(prefix, 'site-packages')
     230  
     231      early_prefix = prefix
     232  
     233      if prefix is None:
     234          if standard_lib:
     235              prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
     236          else:
     237              prefix = plat_specific and EXEC_PREFIX or PREFIX
     238  
     239      if os.name == "posix":
     240          if plat_specific or standard_lib:
     241              # Platform-specific modules (any module from a non-pure-Python
     242              # module distribution) or standard Python library modules.
     243              libdir = getattr(sys, "platlibdir", "lib")
     244          else:
     245              # Pure Python
     246              libdir = "lib"
     247          implementation = 'pypy' if IS_PYPY else 'python'
     248          libpython = os.path.join(prefix, libdir, implementation + get_python_version())
     249          return _posix_lib(standard_lib, libpython, early_prefix, prefix)
     250      elif os.name == "nt":
     251          if standard_lib:
     252              return os.path.join(prefix, "Lib")
     253          else:
     254              return os.path.join(prefix, "Lib", "site-packages")
     255      else:
     256          raise DistutilsPlatformError(
     257              "I don't know where Python installs its library "
     258              "on platform '%s'" % os.name
     259          )
     260  
     261  
     262  def customize_compiler(compiler):  # noqa: C901
     263      """Do any platform-specific customization of a CCompiler instance.
     264  
     265      Mainly needed on Unix, so we can plug in the information that
     266      varies across Unices and is stored in Python's Makefile.
     267      """
     268      if compiler.compiler_type == "unix":
     269          if sys.platform == "darwin":
     270              # Perform first-time customization of compiler-related
     271              # config vars on OS X now that we know we need a compiler.
     272              # This is primarily to support Pythons from binary
     273              # installers.  The kind and paths to build tools on
     274              # the user system may vary significantly from the system
     275              # that Python itself was built on.  Also the user OS
     276              # version and build tools may not support the same set
     277              # of CPU architectures for universal builds.
     278              global _config_vars
     279              # Use get_config_var() to ensure _config_vars is initialized.
     280              if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
     281                  import _osx_support
     282  
     283                  _osx_support.customize_compiler(_config_vars)
     284                  _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
     285  
     286          (
     287              cc,
     288              cxx,
     289              cflags,
     290              ccshared,
     291              ldshared,
     292              shlib_suffix,
     293              ar,
     294              ar_flags,
     295          ) = get_config_vars(
     296              'CC',
     297              'CXX',
     298              'CFLAGS',
     299              'CCSHARED',
     300              'LDSHARED',
     301              'SHLIB_SUFFIX',
     302              'AR',
     303              'ARFLAGS',
     304          )
     305  
     306          if 'CC' in os.environ:
     307              newcc = os.environ['CC']
     308              if 'LDSHARED' not in os.environ and ldshared.startswith(cc):
     309                  # If CC is overridden, use that as the default
     310                  #       command for LDSHARED as well
     311                  ldshared = newcc + ldshared[len(cc) :]
     312              cc = newcc
     313          if 'CXX' in os.environ:
     314              cxx = os.environ['CXX']
     315          if 'LDSHARED' in os.environ:
     316              ldshared = os.environ['LDSHARED']
     317          if 'CPP' in os.environ:
     318              cpp = os.environ['CPP']
     319          else:
     320              cpp = cc + " -E"  # not always
     321          if 'LDFLAGS' in os.environ:
     322              ldshared = ldshared + ' ' + os.environ['LDFLAGS']
     323          if 'CFLAGS' in os.environ:
     324              cflags = cflags + ' ' + os.environ['CFLAGS']
     325              ldshared = ldshared + ' ' + os.environ['CFLAGS']
     326          if 'CPPFLAGS' in os.environ:
     327              cpp = cpp + ' ' + os.environ['CPPFLAGS']
     328              cflags = cflags + ' ' + os.environ['CPPFLAGS']
     329              ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
     330          if 'AR' in os.environ:
     331              ar = os.environ['AR']
     332          if 'ARFLAGS' in os.environ:
     333              archiver = ar + ' ' + os.environ['ARFLAGS']
     334          else:
     335              archiver = ar + ' ' + ar_flags
     336  
     337          cc_cmd = cc + ' ' + cflags
     338          compiler.set_executables(
     339              preprocessor=cpp,
     340              compiler=cc_cmd,
     341              compiler_so=cc_cmd + ' ' + ccshared,
     342              compiler_cxx=cxx,
     343              linker_so=ldshared,
     344              linker_exe=cc,
     345              archiver=archiver,
     346          )
     347  
     348          if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None):
     349              compiler.set_executables(ranlib=os.environ['RANLIB'])
     350  
     351          compiler.shared_lib_extension = shlib_suffix
     352  
     353  
     354  def get_config_h_filename():
     355      """Return full pathname of installed pyconfig.h file."""
     356      if python_build:
     357          if os.name == "nt":
     358              inc_dir = os.path.join(_sys_home or project_base, "PC")
     359          else:
     360              inc_dir = _sys_home or project_base
     361          return os.path.join(inc_dir, 'pyconfig.h')
     362      else:
     363          return sysconfig.get_config_h_filename()
     364  
     365  
     366  def get_makefile_filename():
     367      """Return full pathname of installed Makefile from the Python build."""
     368      return sysconfig.get_makefile_filename()
     369  
     370  
     371  def parse_config_h(fp, g=None):
     372      """Parse a config.h-style file.
     373  
     374      A dictionary containing name/value pairs is returned.  If an
     375      optional dictionary is passed in as the second argument, it is
     376      used instead of a new dictionary.
     377      """
     378      return sysconfig.parse_config_h(fp, vars=g)
     379  
     380  
     381  # Regexes needed for parsing Makefile (and similar syntaxes,
     382  # like old-style Setup files).
     383  _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
     384  _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
     385  _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
     386  
     387  
     388  def parse_makefile(fn, g=None):  # noqa: C901
     389      """Parse a Makefile-style file.
     390  
     391      A dictionary containing name/value pairs is returned.  If an
     392      optional dictionary is passed in as the second argument, it is
     393      used instead of a new dictionary.
     394      """
     395      from distutils.text_file import TextFile
     396  
     397      fp = TextFile(
     398          fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape"
     399      )
     400  
     401      if g is None:
     402          g = {}
     403      done = {}
     404      notdone = {}
     405  
     406      while True:
     407          line = fp.readline()
     408          if line is None:  # eof
     409              break
     410          m = _variable_rx.match(line)
     411          if m:
     412              n, v = m.group(1, 2)
     413              v = v.strip()
     414              # `$$' is a literal `$' in make
     415              tmpv = v.replace('$$', '')
     416  
     417              if "$" in tmpv:
     418                  notdone[n] = v
     419              else:
     420                  try:
     421                      v = int(v)
     422                  except ValueError:
     423                      # insert literal `$'
     424                      done[n] = v.replace('$$', '$')
     425                  else:
     426                      done[n] = v
     427  
     428      # Variables with a 'PY_' prefix in the makefile. These need to
     429      # be made available without that prefix through sysconfig.
     430      # Special care is needed to ensure that variable expansion works, even
     431      # if the expansion uses the name without a prefix.
     432      renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
     433  
     434      # do variable interpolation here
     435      while notdone:
     436          for name in list(notdone):
     437              value = notdone[name]
     438              m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
     439              if m:
     440                  n = m.group(1)
     441                  found = True
     442                  if n in done:
     443                      item = str(done[n])
     444                  elif n in notdone:
     445                      # get it on a subsequent round
     446                      found = False
     447                  elif n in os.environ:
     448                      # do it like make: fall back to environment
     449                      item = os.environ[n]
     450  
     451                  elif n in renamed_variables:
     452                      if name.startswith('PY_') and name[3:] in renamed_variables:
     453                          item = ""
     454  
     455                      elif 'PY_' + n in notdone:
     456                          found = False
     457  
     458                      else:
     459                          item = str(done['PY_' + n])
     460                  else:
     461                      done[n] = item = ""
     462                  if found:
     463                      after = value[m.end() :]
     464                      value = value[: m.start()] + item + after
     465                      if "$" in after:
     466                          notdone[name] = value
     467                      else:
     468                          try:
     469                              value = int(value)
     470                          except ValueError:
     471                              done[name] = value.strip()
     472                          else:
     473                              done[name] = value
     474                          del notdone[name]
     475  
     476                          if name.startswith('PY_') and name[3:] in renamed_variables:
     477  
     478                              name = name[3:]
     479                              if name not in done:
     480                                  done[name] = value
     481              else:
     482                  # bogus variable reference; just drop it since we can't deal
     483                  del notdone[name]
     484  
     485      fp.close()
     486  
     487      # strip spurious spaces
     488      for k, v in done.items():
     489          if isinstance(v, str):
     490              done[k] = v.strip()
     491  
     492      # save the results in the global dictionary
     493      g.update(done)
     494      return g
     495  
     496  
     497  def expand_makefile_vars(s, vars):
     498      """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
     499      'string' according to 'vars' (a dictionary mapping variable names to
     500      values).  Variables not present in 'vars' are silently expanded to the
     501      empty string.  The variable values in 'vars' should not contain further
     502      variable expansions; if 'vars' is the output of 'parse_makefile()',
     503      you're fine.  Returns a variable-expanded version of 's'.
     504      """
     505  
     506      # This algorithm does multiple expansion, so if vars['foo'] contains
     507      # "${bar}", it will expand ${foo} to ${bar}, and then expand
     508      # ${bar}... and so forth.  This is fine as long as 'vars' comes from
     509      # 'parse_makefile()', which takes care of such expansions eagerly,
     510      # according to make's variable expansion semantics.
     511  
     512      while True:
     513          m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
     514          if m:
     515              (beg, end) = m.span()
     516              s = s[0:beg] + vars.get(m.group(1)) + s[end:]
     517          else:
     518              break
     519      return s
     520  
     521  
     522  _config_vars = None
     523  
     524  
     525  def get_config_vars(*args):
     526      """With no arguments, return a dictionary of all configuration
     527      variables relevant for the current platform.  Generally this includes
     528      everything needed to build extensions and install both pure modules and
     529      extensions.  On Unix, this means every variable defined in Python's
     530      installed Makefile; on Windows it's a much smaller set.
     531  
     532      With arguments, return a list of values that result from looking up
     533      each argument in the configuration variable dictionary.
     534      """
     535      global _config_vars
     536      if _config_vars is None:
     537          _config_vars = sysconfig.get_config_vars().copy()
     538          py39compat.add_ext_suffix(_config_vars)
     539  
     540      if args:
     541          vals = []
     542          for name in args:
     543              vals.append(_config_vars.get(name))
     544          return vals
     545      else:
     546          return _config_vars
     547  
     548  
     549  def get_config_var(name):
     550      """Return the value of a single variable using the dictionary
     551      returned by 'get_config_vars()'.  Equivalent to
     552      get_config_vars().get(name)
     553      """
     554      if name == 'SO':
     555          import warnings
     556  
     557          warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
     558      return get_config_vars().get(name)