(root)/
Python-3.11.7/
Lib/
distutils/
command/
build_clib.py
       1  """distutils.command.build_clib
       2  
       3  Implements the Distutils 'build_clib' command, to build a C/C++ library
       4  that is included in the module distribution and needed by an extension
       5  module."""
       6  
       7  
       8  # XXX this module has *lots* of code ripped-off quite transparently from
       9  # build_ext.py -- not surprisingly really, as the work required to build
      10  # a static library from a collection of C source files is not really all
      11  # that different from what's required to build a shared object file from
      12  # a collection of C source files.  Nevertheless, I haven't done the
      13  # necessary refactoring to account for the overlap in code between the
      14  # two modules, mainly because a number of subtle details changed in the
      15  # cut 'n paste.  Sigh.
      16  
      17  import os
      18  from distutils.core import Command
      19  from distutils.errors import *
      20  from distutils.sysconfig import customize_compiler
      21  from distutils import log
      22  
      23  def show_compilers():
      24      from distutils.ccompiler import show_compilers
      25      show_compilers()
      26  
      27  
      28  class ESC[4;38;5;81mbuild_clib(ESC[4;38;5;149mCommand):
      29  
      30      description = "build C/C++ libraries used by Python extensions"
      31  
      32      user_options = [
      33          ('build-clib=', 'b',
      34           "directory to build C/C++ libraries to"),
      35          ('build-temp=', 't',
      36           "directory to put temporary build by-products"),
      37          ('debug', 'g',
      38           "compile with debugging information"),
      39          ('force', 'f',
      40           "forcibly build everything (ignore file timestamps)"),
      41          ('compiler=', 'c',
      42           "specify the compiler type"),
      43          ]
      44  
      45      boolean_options = ['debug', 'force']
      46  
      47      help_options = [
      48          ('help-compiler', None,
      49           "list available compilers", show_compilers),
      50          ]
      51  
      52      def initialize_options(self):
      53          self.build_clib = None
      54          self.build_temp = None
      55  
      56          # List of libraries to build
      57          self.libraries = None
      58  
      59          # Compilation options for all libraries
      60          self.include_dirs = None
      61          self.define = None
      62          self.undef = None
      63          self.debug = None
      64          self.force = 0
      65          self.compiler = None
      66  
      67  
      68      def finalize_options(self):
      69          # This might be confusing: both build-clib and build-temp default
      70          # to build-temp as defined by the "build" command.  This is because
      71          # I think that C libraries are really just temporary build
      72          # by-products, at least from the point of view of building Python
      73          # extensions -- but I want to keep my options open.
      74          self.set_undefined_options('build',
      75                                     ('build_temp', 'build_clib'),
      76                                     ('build_temp', 'build_temp'),
      77                                     ('compiler', 'compiler'),
      78                                     ('debug', 'debug'),
      79                                     ('force', 'force'))
      80  
      81          self.libraries = self.distribution.libraries
      82          if self.libraries:
      83              self.check_library_list(self.libraries)
      84  
      85          if self.include_dirs is None:
      86              self.include_dirs = self.distribution.include_dirs or []
      87          if isinstance(self.include_dirs, str):
      88              self.include_dirs = self.include_dirs.split(os.pathsep)
      89  
      90          # XXX same as for build_ext -- what about 'self.define' and
      91          # 'self.undef' ?
      92  
      93  
      94      def run(self):
      95          if not self.libraries:
      96              return
      97  
      98          # Yech -- this is cut 'n pasted from build_ext.py!
      99          from distutils.ccompiler import new_compiler
     100          self.compiler = new_compiler(compiler=self.compiler,
     101                                       dry_run=self.dry_run,
     102                                       force=self.force)
     103          customize_compiler(self.compiler)
     104  
     105          if self.include_dirs is not None:
     106              self.compiler.set_include_dirs(self.include_dirs)
     107          if self.define is not None:
     108              # 'define' option is a list of (name,value) tuples
     109              for (name,value) in self.define:
     110                  self.compiler.define_macro(name, value)
     111          if self.undef is not None:
     112              for macro in self.undef:
     113                  self.compiler.undefine_macro(macro)
     114  
     115          self.build_libraries(self.libraries)
     116  
     117  
     118      def check_library_list(self, libraries):
     119          """Ensure that the list of libraries is valid.
     120  
     121          `library` is presumably provided as a command option 'libraries'.
     122          This method checks that it is a list of 2-tuples, where the tuples
     123          are (library_name, build_info_dict).
     124  
     125          Raise DistutilsSetupError if the structure is invalid anywhere;
     126          just returns otherwise.
     127          """
     128          if not isinstance(libraries, list):
     129              raise DistutilsSetupError(
     130                    "'libraries' option must be a list of tuples")
     131  
     132          for lib in libraries:
     133              if not isinstance(lib, tuple) and len(lib) != 2:
     134                  raise DistutilsSetupError(
     135                        "each element of 'libraries' must a 2-tuple")
     136  
     137              name, build_info = lib
     138  
     139              if not isinstance(name, str):
     140                  raise DistutilsSetupError(
     141                        "first element of each tuple in 'libraries' "
     142                        "must be a string (the library name)")
     143  
     144              if '/' in name or (os.sep != '/' and os.sep in name):
     145                  raise DistutilsSetupError("bad library name '%s': "
     146                         "may not contain directory separators" % lib[0])
     147  
     148              if not isinstance(build_info, dict):
     149                  raise DistutilsSetupError(
     150                        "second element of each tuple in 'libraries' "
     151                        "must be a dictionary (build info)")
     152  
     153  
     154      def get_library_names(self):
     155          # Assume the library list is valid -- 'check_library_list()' is
     156          # called from 'finalize_options()', so it should be!
     157          if not self.libraries:
     158              return None
     159  
     160          lib_names = []
     161          for (lib_name, build_info) in self.libraries:
     162              lib_names.append(lib_name)
     163          return lib_names
     164  
     165  
     166      def get_source_files(self):
     167          self.check_library_list(self.libraries)
     168          filenames = []
     169          for (lib_name, build_info) in self.libraries:
     170              sources = build_info.get('sources')
     171              if sources is None or not isinstance(sources, (list, tuple)):
     172                  raise DistutilsSetupError(
     173                         "in 'libraries' option (library '%s'), "
     174                         "'sources' must be present and must be "
     175                         "a list of source filenames" % lib_name)
     176  
     177              filenames.extend(sources)
     178          return filenames
     179  
     180  
     181      def build_libraries(self, libraries):
     182          for (lib_name, build_info) in libraries:
     183              sources = build_info.get('sources')
     184              if sources is None or not isinstance(sources, (list, tuple)):
     185                  raise DistutilsSetupError(
     186                         "in 'libraries' option (library '%s'), "
     187                         "'sources' must be present and must be "
     188                         "a list of source filenames" % lib_name)
     189              sources = list(sources)
     190  
     191              log.info("building '%s' library", lib_name)
     192  
     193              # First, compile the source code to object files in the library
     194              # directory.  (This should probably change to putting object
     195              # files in a temporary build directory.)
     196              macros = build_info.get('macros')
     197              include_dirs = build_info.get('include_dirs')
     198              objects = self.compiler.compile(sources,
     199                                              output_dir=self.build_temp,
     200                                              macros=macros,
     201                                              include_dirs=include_dirs,
     202                                              debug=self.debug)
     203  
     204              # Now "link" the object files together into a static library.
     205              # (On Unix at least, this isn't really linking -- it just
     206              # builds an archive.  Whatever.)
     207              self.compiler.create_static_lib(objects, lib_name,
     208                                              output_dir=self.build_clib,
     209                                              debug=self.debug)