Home | History | Annotate | Download | only in command
      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 build_clib(Command):
     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)
    210