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