python (3.11.7)

(root)/
lib/
python3.11/
site-packages/
setuptools/
_distutils/
command/
build_ext.py
       1  """distutils.command.build_ext
       2  
       3  Implements the Distutils 'build_ext' command, for building extension
       4  modules (currently limited to C extensions, should accommodate C++
       5  extensions ASAP)."""
       6  
       7  import contextlib
       8  import os
       9  import re
      10  import sys
      11  from distutils.core import Command
      12  from distutils.errors import (
      13      DistutilsOptionError,
      14      DistutilsSetupError,
      15      CCompilerError,
      16      DistutilsError,
      17      CompileError,
      18      DistutilsPlatformError,
      19  )
      20  from distutils.sysconfig import customize_compiler, get_python_version
      21  from distutils.sysconfig import get_config_h_filename
      22  from distutils.dep_util import newer_group
      23  from distutils.extension import Extension
      24  from distutils.util import get_platform
      25  from distutils import log
      26  from . import py37compat
      27  
      28  from site import USER_BASE
      29  
      30  # An extension name is just a dot-separated list of Python NAMEs (ie.
      31  # the same as a fully-qualified module name).
      32  extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
      33  
      34  
      35  def show_compilers():
      36      from distutils.ccompiler import show_compilers
      37  
      38      show_compilers()
      39  
      40  
      41  class ESC[4;38;5;81mbuild_ext(ESC[4;38;5;149mCommand):
      42  
      43      description = "build C/C++ extensions (compile/link to build directory)"
      44  
      45      # XXX thoughts on how to deal with complex command-line options like
      46      # these, i.e. how to make it so fancy_getopt can suck them off the
      47      # command line and make it look like setup.py defined the appropriate
      48      # lists of tuples of what-have-you.
      49      #   - each command needs a callback to process its command-line options
      50      #   - Command.__init__() needs access to its share of the whole
      51      #     command line (must ultimately come from
      52      #     Distribution.parse_command_line())
      53      #   - it then calls the current command class' option-parsing
      54      #     callback to deal with weird options like -D, which have to
      55      #     parse the option text and churn out some custom data
      56      #     structure
      57      #   - that data structure (in this case, a list of 2-tuples)
      58      #     will then be present in the command object by the time
      59      #     we get to finalize_options() (i.e. the constructor
      60      #     takes care of both command-line and client options
      61      #     in between initialize_options() and finalize_options())
      62  
      63      sep_by = " (separated by '%s')" % os.pathsep
      64      user_options = [
      65          ('build-lib=', 'b', "directory for compiled extension modules"),
      66          ('build-temp=', 't', "directory for temporary files (build by-products)"),
      67          (
      68              'plat-name=',
      69              'p',
      70              "platform name to cross-compile for, if supported "
      71              "(default: %s)" % get_platform(),
      72          ),
      73          (
      74              'inplace',
      75              'i',
      76              "ignore build-lib and put compiled extensions into the source "
      77              + "directory alongside your pure Python modules",
      78          ),
      79          (
      80              'include-dirs=',
      81              'I',
      82              "list of directories to search for header files" + sep_by,
      83          ),
      84          ('define=', 'D', "C preprocessor macros to define"),
      85          ('undef=', 'U', "C preprocessor macros to undefine"),
      86          ('libraries=', 'l', "external C libraries to link with"),
      87          (
      88              'library-dirs=',
      89              'L',
      90              "directories to search for external C libraries" + sep_by,
      91          ),
      92          ('rpath=', 'R', "directories to search for shared C libraries at runtime"),
      93          ('link-objects=', 'O', "extra explicit link objects to include in the link"),
      94          ('debug', 'g', "compile/link with debugging information"),
      95          ('force', 'f', "forcibly build everything (ignore file timestamps)"),
      96          ('compiler=', 'c', "specify the compiler type"),
      97          ('parallel=', 'j', "number of parallel build jobs"),
      98          ('swig-cpp', None, "make SWIG create C++ files (default is C)"),
      99          ('swig-opts=', None, "list of SWIG command line options"),
     100          ('swig=', None, "path to the SWIG executable"),
     101          ('user', None, "add user include, library and rpath"),
     102      ]
     103  
     104      boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user']
     105  
     106      help_options = [
     107          ('help-compiler', None, "list available compilers", show_compilers),
     108      ]
     109  
     110      def initialize_options(self):
     111          self.extensions = None
     112          self.build_lib = None
     113          self.plat_name = None
     114          self.build_temp = None
     115          self.inplace = 0
     116          self.package = None
     117  
     118          self.include_dirs = None
     119          self.define = None
     120          self.undef = None
     121          self.libraries = None
     122          self.library_dirs = None
     123          self.rpath = None
     124          self.link_objects = None
     125          self.debug = None
     126          self.force = None
     127          self.compiler = None
     128          self.swig = None
     129          self.swig_cpp = None
     130          self.swig_opts = None
     131          self.user = None
     132          self.parallel = None
     133  
     134      def finalize_options(self):  # noqa: C901
     135          from distutils import sysconfig
     136  
     137          self.set_undefined_options(
     138              'build',
     139              ('build_lib', 'build_lib'),
     140              ('build_temp', 'build_temp'),
     141              ('compiler', 'compiler'),
     142              ('debug', 'debug'),
     143              ('force', 'force'),
     144              ('parallel', 'parallel'),
     145              ('plat_name', 'plat_name'),
     146          )
     147  
     148          if self.package is None:
     149              self.package = self.distribution.ext_package
     150  
     151          self.extensions = self.distribution.ext_modules
     152  
     153          # Make sure Python's include directories (for Python.h, pyconfig.h,
     154          # etc.) are in the include search path.
     155          py_include = sysconfig.get_python_inc()
     156          plat_py_include = sysconfig.get_python_inc(plat_specific=1)
     157          if self.include_dirs is None:
     158              self.include_dirs = self.distribution.include_dirs or []
     159          if isinstance(self.include_dirs, str):
     160              self.include_dirs = self.include_dirs.split(os.pathsep)
     161  
     162          # If in a virtualenv, add its include directory
     163          # Issue 16116
     164          if sys.exec_prefix != sys.base_exec_prefix:
     165              self.include_dirs.append(os.path.join(sys.exec_prefix, 'include'))
     166  
     167          # Put the Python "system" include dir at the end, so that
     168          # any local include dirs take precedence.
     169          self.include_dirs.extend(py_include.split(os.path.pathsep))
     170          if plat_py_include != py_include:
     171              self.include_dirs.extend(plat_py_include.split(os.path.pathsep))
     172  
     173          self.ensure_string_list('libraries')
     174          self.ensure_string_list('link_objects')
     175  
     176          # Life is easier if we're not forever checking for None, so
     177          # simplify these options to empty lists if unset
     178          if self.libraries is None:
     179              self.libraries = []
     180          if self.library_dirs is None:
     181              self.library_dirs = []
     182          elif isinstance(self.library_dirs, str):
     183              self.library_dirs = self.library_dirs.split(os.pathsep)
     184  
     185          if self.rpath is None:
     186              self.rpath = []
     187          elif isinstance(self.rpath, str):
     188              self.rpath = self.rpath.split(os.pathsep)
     189  
     190          # for extensions under windows use different directories
     191          # for Release and Debug builds.
     192          # also Python's library directory must be appended to library_dirs
     193          if os.name == 'nt':
     194              # the 'libs' directory is for binary installs - we assume that
     195              # must be the *native* platform.  But we don't really support
     196              # cross-compiling via a binary install anyway, so we let it go.
     197              self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs'))
     198              if sys.base_exec_prefix != sys.prefix:  # Issue 16116
     199                  self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs'))
     200              if self.debug:
     201                  self.build_temp = os.path.join(self.build_temp, "Debug")
     202              else:
     203                  self.build_temp = os.path.join(self.build_temp, "Release")
     204  
     205              # Append the source distribution include and library directories,
     206              # this allows distutils on windows to work in the source tree
     207              self.include_dirs.append(os.path.dirname(get_config_h_filename()))
     208              self.library_dirs.append(sys.base_exec_prefix)
     209  
     210              # Use the .lib files for the correct architecture
     211              if self.plat_name == 'win32':
     212                  suffix = 'win32'
     213              else:
     214                  # win-amd64
     215                  suffix = self.plat_name[4:]
     216              new_lib = os.path.join(sys.exec_prefix, 'PCbuild')
     217              if suffix:
     218                  new_lib = os.path.join(new_lib, suffix)
     219              self.library_dirs.append(new_lib)
     220  
     221          # For extensions under Cygwin, Python's library directory must be
     222          # appended to library_dirs
     223          if sys.platform[:6] == 'cygwin':
     224              if not sysconfig.python_build:
     225                  # building third party extensions
     226                  self.library_dirs.append(
     227                      os.path.join(
     228                          sys.prefix, "lib", "python" + get_python_version(), "config"
     229                      )
     230                  )
     231              else:
     232                  # building python standard extensions
     233                  self.library_dirs.append('.')
     234  
     235          # For building extensions with a shared Python library,
     236          # Python's library directory must be appended to library_dirs
     237          # See Issues: #1600860, #4366
     238          if sysconfig.get_config_var('Py_ENABLE_SHARED'):
     239              if not sysconfig.python_build:
     240                  # building third party extensions
     241                  self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
     242              else:
     243                  # building python standard extensions
     244                  self.library_dirs.append('.')
     245  
     246          # The argument parsing will result in self.define being a string, but
     247          # it has to be a list of 2-tuples.  All the preprocessor symbols
     248          # specified by the 'define' option will be set to '1'.  Multiple
     249          # symbols can be separated with commas.
     250  
     251          if self.define:
     252              defines = self.define.split(',')
     253              self.define = [(symbol, '1') for symbol in defines]
     254  
     255          # The option for macros to undefine is also a string from the
     256          # option parsing, but has to be a list.  Multiple symbols can also
     257          # be separated with commas here.
     258          if self.undef:
     259              self.undef = self.undef.split(',')
     260  
     261          if self.swig_opts is None:
     262              self.swig_opts = []
     263          else:
     264              self.swig_opts = self.swig_opts.split(' ')
     265  
     266          # Finally add the user include and library directories if requested
     267          if self.user:
     268              user_include = os.path.join(USER_BASE, "include")
     269              user_lib = os.path.join(USER_BASE, "lib")
     270              if os.path.isdir(user_include):
     271                  self.include_dirs.append(user_include)
     272              if os.path.isdir(user_lib):
     273                  self.library_dirs.append(user_lib)
     274                  self.rpath.append(user_lib)
     275  
     276          if isinstance(self.parallel, str):
     277              try:
     278                  self.parallel = int(self.parallel)
     279              except ValueError:
     280                  raise DistutilsOptionError("parallel should be an integer")
     281  
     282      def run(self):  # noqa: C901
     283          from distutils.ccompiler import new_compiler
     284  
     285          # 'self.extensions', as supplied by setup.py, is a list of
     286          # Extension instances.  See the documentation for Extension (in
     287          # distutils.extension) for details.
     288          #
     289          # For backwards compatibility with Distutils 0.8.2 and earlier, we
     290          # also allow the 'extensions' list to be a list of tuples:
     291          #    (ext_name, build_info)
     292          # where build_info is a dictionary containing everything that
     293          # Extension instances do except the name, with a few things being
     294          # differently named.  We convert these 2-tuples to Extension
     295          # instances as needed.
     296  
     297          if not self.extensions:
     298              return
     299  
     300          # If we were asked to build any C/C++ libraries, make sure that the
     301          # directory where we put them is in the library search path for
     302          # linking extensions.
     303          if self.distribution.has_c_libraries():
     304              build_clib = self.get_finalized_command('build_clib')
     305              self.libraries.extend(build_clib.get_library_names() or [])
     306              self.library_dirs.append(build_clib.build_clib)
     307  
     308          # Setup the CCompiler object that we'll use to do all the
     309          # compiling and linking
     310          self.compiler = new_compiler(
     311              compiler=self.compiler,
     312              verbose=self.verbose,
     313              dry_run=self.dry_run,
     314              force=self.force,
     315          )
     316          customize_compiler(self.compiler)
     317          # If we are cross-compiling, init the compiler now (if we are not
     318          # cross-compiling, init would not hurt, but people may rely on
     319          # late initialization of compiler even if they shouldn't...)
     320          if os.name == 'nt' and self.plat_name != get_platform():
     321              self.compiler.initialize(self.plat_name)
     322  
     323          # And make sure that any compile/link-related options (which might
     324          # come from the command-line or from the setup script) are set in
     325          # that CCompiler object -- that way, they automatically apply to
     326          # all compiling and linking done here.
     327          if self.include_dirs is not None:
     328              self.compiler.set_include_dirs(self.include_dirs)
     329          if self.define is not None:
     330              # 'define' option is a list of (name,value) tuples
     331              for (name, value) in self.define:
     332                  self.compiler.define_macro(name, value)
     333          if self.undef is not None:
     334              for macro in self.undef:
     335                  self.compiler.undefine_macro(macro)
     336          if self.libraries is not None:
     337              self.compiler.set_libraries(self.libraries)
     338          if self.library_dirs is not None:
     339              self.compiler.set_library_dirs(self.library_dirs)
     340          if self.rpath is not None:
     341              self.compiler.set_runtime_library_dirs(self.rpath)
     342          if self.link_objects is not None:
     343              self.compiler.set_link_objects(self.link_objects)
     344  
     345          # Now actually compile and link everything.
     346          self.build_extensions()
     347  
     348      def check_extensions_list(self, extensions):  # noqa: C901
     349          """Ensure that the list of extensions (presumably provided as a
     350          command option 'extensions') is valid, i.e. it is a list of
     351          Extension objects.  We also support the old-style list of 2-tuples,
     352          where the tuples are (ext_name, build_info), which are converted to
     353          Extension instances here.
     354  
     355          Raise DistutilsSetupError if the structure is invalid anywhere;
     356          just returns otherwise.
     357          """
     358          if not isinstance(extensions, list):
     359              raise DistutilsSetupError(
     360                  "'ext_modules' option must be a list of Extension instances"
     361              )
     362  
     363          for i, ext in enumerate(extensions):
     364              if isinstance(ext, Extension):
     365                  continue  # OK! (assume type-checking done
     366                  # by Extension constructor)
     367  
     368              if not isinstance(ext, tuple) or len(ext) != 2:
     369                  raise DistutilsSetupError(
     370                      "each element of 'ext_modules' option must be an "
     371                      "Extension instance or 2-tuple"
     372                  )
     373  
     374              ext_name, build_info = ext
     375  
     376              log.warn(
     377                  "old-style (ext_name, build_info) tuple found in "
     378                  "ext_modules for extension '%s' "
     379                  "-- please convert to Extension instance",
     380                  ext_name,
     381              )
     382  
     383              if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)):
     384                  raise DistutilsSetupError(
     385                      "first element of each tuple in 'ext_modules' "
     386                      "must be the extension name (a string)"
     387                  )
     388  
     389              if not isinstance(build_info, dict):
     390                  raise DistutilsSetupError(
     391                      "second element of each tuple in 'ext_modules' "
     392                      "must be a dictionary (build info)"
     393                  )
     394  
     395              # OK, the (ext_name, build_info) dict is type-safe: convert it
     396              # to an Extension instance.
     397              ext = Extension(ext_name, build_info['sources'])
     398  
     399              # Easy stuff: one-to-one mapping from dict elements to
     400              # instance attributes.
     401              for key in (
     402                  'include_dirs',
     403                  'library_dirs',
     404                  'libraries',
     405                  'extra_objects',
     406                  'extra_compile_args',
     407                  'extra_link_args',
     408              ):
     409                  val = build_info.get(key)
     410                  if val is not None:
     411                      setattr(ext, key, val)
     412  
     413              # Medium-easy stuff: same syntax/semantics, different names.
     414              ext.runtime_library_dirs = build_info.get('rpath')
     415              if 'def_file' in build_info:
     416                  log.warn("'def_file' element of build info dict " "no longer supported")
     417  
     418              # Non-trivial stuff: 'macros' split into 'define_macros'
     419              # and 'undef_macros'.
     420              macros = build_info.get('macros')
     421              if macros:
     422                  ext.define_macros = []
     423                  ext.undef_macros = []
     424                  for macro in macros:
     425                      if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
     426                          raise DistutilsSetupError(
     427                              "'macros' element of build info dict "
     428                              "must be 1- or 2-tuple"
     429                          )
     430                      if len(macro) == 1:
     431                          ext.undef_macros.append(macro[0])
     432                      elif len(macro) == 2:
     433                          ext.define_macros.append(macro)
     434  
     435              extensions[i] = ext
     436  
     437      def get_source_files(self):
     438          self.check_extensions_list(self.extensions)
     439          filenames = []
     440  
     441          # Wouldn't it be neat if we knew the names of header files too...
     442          for ext in self.extensions:
     443              filenames.extend(ext.sources)
     444          return filenames
     445  
     446      def get_outputs(self):
     447          # Sanity check the 'extensions' list -- can't assume this is being
     448          # done in the same run as a 'build_extensions()' call (in fact, we
     449          # can probably assume that it *isn't*!).
     450          self.check_extensions_list(self.extensions)
     451  
     452          # And build the list of output (built) filenames.  Note that this
     453          # ignores the 'inplace' flag, and assumes everything goes in the
     454          # "build" tree.
     455          outputs = []
     456          for ext in self.extensions:
     457              outputs.append(self.get_ext_fullpath(ext.name))
     458          return outputs
     459  
     460      def build_extensions(self):
     461          # First, sanity-check the 'extensions' list
     462          self.check_extensions_list(self.extensions)
     463          if self.parallel:
     464              self._build_extensions_parallel()
     465          else:
     466              self._build_extensions_serial()
     467  
     468      def _build_extensions_parallel(self):
     469          workers = self.parallel
     470          if self.parallel is True:
     471              workers = os.cpu_count()  # may return None
     472          try:
     473              from concurrent.futures import ThreadPoolExecutor
     474          except ImportError:
     475              workers = None
     476  
     477          if workers is None:
     478              self._build_extensions_serial()
     479              return
     480  
     481          with ThreadPoolExecutor(max_workers=workers) as executor:
     482              futures = [
     483                  executor.submit(self.build_extension, ext) for ext in self.extensions
     484              ]
     485              for ext, fut in zip(self.extensions, futures):
     486                  with self._filter_build_errors(ext):
     487                      fut.result()
     488  
     489      def _build_extensions_serial(self):
     490          for ext in self.extensions:
     491              with self._filter_build_errors(ext):
     492                  self.build_extension(ext)
     493  
     494      @contextlib.contextmanager
     495      def _filter_build_errors(self, ext):
     496          try:
     497              yield
     498          except (CCompilerError, DistutilsError, CompileError) as e:
     499              if not ext.optional:
     500                  raise
     501              self.warn('building extension "{}" failed: {}'.format(ext.name, e))
     502  
     503      def build_extension(self, ext):
     504          sources = ext.sources
     505          if sources is None or not isinstance(sources, (list, tuple)):
     506              raise DistutilsSetupError(
     507                  "in 'ext_modules' option (extension '%s'), "
     508                  "'sources' must be present and must be "
     509                  "a list of source filenames" % ext.name
     510              )
     511          # sort to make the resulting .so file build reproducible
     512          sources = sorted(sources)
     513  
     514          ext_path = self.get_ext_fullpath(ext.name)
     515          depends = sources + ext.depends
     516          if not (self.force or newer_group(depends, ext_path, 'newer')):
     517              log.debug("skipping '%s' extension (up-to-date)", ext.name)
     518              return
     519          else:
     520              log.info("building '%s' extension", ext.name)
     521  
     522          # First, scan the sources for SWIG definition files (.i), run
     523          # SWIG on 'em to create .c files, and modify the sources list
     524          # accordingly.
     525          sources = self.swig_sources(sources, ext)
     526  
     527          # Next, compile the source code to object files.
     528  
     529          # XXX not honouring 'define_macros' or 'undef_macros' -- the
     530          # CCompiler API needs to change to accommodate this, and I
     531          # want to do one thing at a time!
     532  
     533          # Two possible sources for extra compiler arguments:
     534          #   - 'extra_compile_args' in Extension object
     535          #   - CFLAGS environment variable (not particularly
     536          #     elegant, but people seem to expect it and I
     537          #     guess it's useful)
     538          # The environment variable should take precedence, and
     539          # any sensible compiler will give precedence to later
     540          # command line args.  Hence we combine them in order:
     541          extra_args = ext.extra_compile_args or []
     542  
     543          macros = ext.define_macros[:]
     544          for undef in ext.undef_macros:
     545              macros.append((undef,))
     546  
     547          objects = self.compiler.compile(
     548              sources,
     549              output_dir=self.build_temp,
     550              macros=macros,
     551              include_dirs=ext.include_dirs,
     552              debug=self.debug,
     553              extra_postargs=extra_args,
     554              depends=ext.depends,
     555          )
     556  
     557          # XXX outdated variable, kept here in case third-part code
     558          # needs it.
     559          self._built_objects = objects[:]
     560  
     561          # Now link the object files together into a "shared object" --
     562          # of course, first we have to figure out all the other things
     563          # that go into the mix.
     564          if ext.extra_objects:
     565              objects.extend(ext.extra_objects)
     566          extra_args = ext.extra_link_args or []
     567  
     568          # Detect target language, if not provided
     569          language = ext.language or self.compiler.detect_language(sources)
     570  
     571          self.compiler.link_shared_object(
     572              objects,
     573              ext_path,
     574              libraries=self.get_libraries(ext),
     575              library_dirs=ext.library_dirs,
     576              runtime_library_dirs=ext.runtime_library_dirs,
     577              extra_postargs=extra_args,
     578              export_symbols=self.get_export_symbols(ext),
     579              debug=self.debug,
     580              build_temp=self.build_temp,
     581              target_lang=language,
     582          )
     583  
     584      def swig_sources(self, sources, extension):
     585          """Walk the list of source files in 'sources', looking for SWIG
     586          interface (.i) files.  Run SWIG on all that are found, and
     587          return a modified 'sources' list with SWIG source files replaced
     588          by the generated C (or C++) files.
     589          """
     590          new_sources = []
     591          swig_sources = []
     592          swig_targets = {}
     593  
     594          # XXX this drops generated C/C++ files into the source tree, which
     595          # is fine for developers who want to distribute the generated
     596          # source -- but there should be an option to put SWIG output in
     597          # the temp dir.
     598  
     599          if self.swig_cpp:
     600              log.warn("--swig-cpp is deprecated - use --swig-opts=-c++")
     601  
     602          if (
     603              self.swig_cpp
     604              or ('-c++' in self.swig_opts)
     605              or ('-c++' in extension.swig_opts)
     606          ):
     607              target_ext = '.cpp'
     608          else:
     609              target_ext = '.c'
     610  
     611          for source in sources:
     612              (base, ext) = os.path.splitext(source)
     613              if ext == ".i":  # SWIG interface file
     614                  new_sources.append(base + '_wrap' + target_ext)
     615                  swig_sources.append(source)
     616                  swig_targets[source] = new_sources[-1]
     617              else:
     618                  new_sources.append(source)
     619  
     620          if not swig_sources:
     621              return new_sources
     622  
     623          swig = self.swig or self.find_swig()
     624          swig_cmd = [swig, "-python"]
     625          swig_cmd.extend(self.swig_opts)
     626          if self.swig_cpp:
     627              swig_cmd.append("-c++")
     628  
     629          # Do not override commandline arguments
     630          if not self.swig_opts:
     631              for o in extension.swig_opts:
     632                  swig_cmd.append(o)
     633  
     634          for source in swig_sources:
     635              target = swig_targets[source]
     636              log.info("swigging %s to %s", source, target)
     637              self.spawn(swig_cmd + ["-o", target, source])
     638  
     639          return new_sources
     640  
     641      def find_swig(self):
     642          """Return the name of the SWIG executable.  On Unix, this is
     643          just "swig" -- it should be in the PATH.  Tries a bit harder on
     644          Windows.
     645          """
     646          if os.name == "posix":
     647              return "swig"
     648          elif os.name == "nt":
     649              # Look for SWIG in its standard installation directory on
     650              # Windows (or so I presume!).  If we find it there, great;
     651              # if not, act like Unix and assume it's in the PATH.
     652              for vers in ("1.3", "1.2", "1.1"):
     653                  fn = os.path.join("c:\\swig%s" % vers, "swig.exe")
     654                  if os.path.isfile(fn):
     655                      return fn
     656              else:
     657                  return "swig.exe"
     658          else:
     659              raise DistutilsPlatformError(
     660                  "I don't know how to find (much less run) SWIG "
     661                  "on platform '%s'" % os.name
     662              )
     663  
     664      # -- Name generators -----------------------------------------------
     665      # (extension names, filenames, whatever)
     666      def get_ext_fullpath(self, ext_name):
     667          """Returns the path of the filename for a given extension.
     668  
     669          The file is located in `build_lib` or directly in the package
     670          (inplace option).
     671          """
     672          fullname = self.get_ext_fullname(ext_name)
     673          modpath = fullname.split('.')
     674          filename = self.get_ext_filename(modpath[-1])
     675  
     676          if not self.inplace:
     677              # no further work needed
     678              # returning :
     679              #   build_dir/package/path/filename
     680              filename = os.path.join(*modpath[:-1] + [filename])
     681              return os.path.join(self.build_lib, filename)
     682  
     683          # the inplace option requires to find the package directory
     684          # using the build_py command for that
     685          package = '.'.join(modpath[0:-1])
     686          build_py = self.get_finalized_command('build_py')
     687          package_dir = os.path.abspath(build_py.get_package_dir(package))
     688  
     689          # returning
     690          #   package_dir/filename
     691          return os.path.join(package_dir, filename)
     692  
     693      def get_ext_fullname(self, ext_name):
     694          """Returns the fullname of a given extension name.
     695  
     696          Adds the `package.` prefix"""
     697          if self.package is None:
     698              return ext_name
     699          else:
     700              return self.package + '.' + ext_name
     701  
     702      def get_ext_filename(self, ext_name):
     703          r"""Convert the name of an extension (eg. "foo.bar") into the name
     704          of the file from which it will be loaded (eg. "foo/bar.so", or
     705          "foo\bar.pyd").
     706          """
     707          from distutils.sysconfig import get_config_var
     708  
     709          ext_path = ext_name.split('.')
     710          ext_suffix = get_config_var('EXT_SUFFIX')
     711          return os.path.join(*ext_path) + ext_suffix
     712  
     713      def get_export_symbols(self, ext):
     714          """Return the list of symbols that a shared extension has to
     715          export.  This either uses 'ext.export_symbols' or, if it's not
     716          provided, "PyInit_" + module_name.  Only relevant on Windows, where
     717          the .pyd file (DLL) must export the module "PyInit_" function.
     718          """
     719          name = ext.name.split('.')[-1]
     720          try:
     721              # Unicode module name support as defined in PEP-489
     722              # https://www.python.org/dev/peps/pep-0489/#export-hook-name
     723              name.encode('ascii')
     724          except UnicodeEncodeError:
     725              suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii')
     726          else:
     727              suffix = "_" + name
     728  
     729          initfunc_name = "PyInit" + suffix
     730          if initfunc_name not in ext.export_symbols:
     731              ext.export_symbols.append(initfunc_name)
     732          return ext.export_symbols
     733  
     734      def get_libraries(self, ext):  # noqa: C901
     735          """Return the list of libraries to link against when building a
     736          shared extension.  On most platforms, this is just 'ext.libraries';
     737          on Windows, we add the Python library (eg. python20.dll).
     738          """
     739          # The python library is always needed on Windows.  For MSVC, this
     740          # is redundant, since the library is mentioned in a pragma in
     741          # pyconfig.h that MSVC groks.  The other Windows compilers all seem
     742          # to need it mentioned explicitly, though, so that's what we do.
     743          # Append '_d' to the python import library on debug builds.
     744          if sys.platform == "win32":
     745              from distutils._msvccompiler import MSVCCompiler
     746  
     747              if not isinstance(self.compiler, MSVCCompiler):
     748                  template = "python%d%d"
     749                  if self.debug:
     750                      template = template + '_d'
     751                  pythonlib = template % (
     752                      sys.hexversion >> 24,
     753                      (sys.hexversion >> 16) & 0xFF,
     754                  )
     755                  # don't extend ext.libraries, it may be shared with other
     756                  # extensions, it is a reference to the original list
     757                  return ext.libraries + [pythonlib]
     758          else:
     759              # On Android only the main executable and LD_PRELOADs are considered
     760              # to be RTLD_GLOBAL, all the dependencies of the main executable
     761              # remain RTLD_LOCAL and so the shared libraries must be linked with
     762              # libpython when python is built with a shared python library (issue
     763              # bpo-21536).
     764              # On Cygwin (and if required, other POSIX-like platforms based on
     765              # Windows like MinGW) it is simply necessary that all symbols in
     766              # shared libraries are resolved at link time.
     767              from distutils.sysconfig import get_config_var
     768  
     769              link_libpython = False
     770              if get_config_var('Py_ENABLE_SHARED'):
     771                  # A native build on an Android device or on Cygwin
     772                  if hasattr(sys, 'getandroidapilevel'):
     773                      link_libpython = True
     774                  elif sys.platform == 'cygwin':
     775                      link_libpython = True
     776                  elif '_PYTHON_HOST_PLATFORM' in os.environ:
     777                      # We are cross-compiling for one of the relevant platforms
     778                      if get_config_var('ANDROID_API_LEVEL') != 0:
     779                          link_libpython = True
     780                      elif get_config_var('MACHDEP') == 'cygwin':
     781                          link_libpython = True
     782  
     783              if link_libpython:
     784                  ldversion = get_config_var('LDVERSION')
     785                  return ext.libraries + ['python' + ldversion]
     786  
     787          return ext.libraries + py37compat.pythonlib()