1 """distutils.command.config 2 3 Implements the Distutils 'config' command, a (mostly) empty command class 4 that exists mainly to be sub-classed by specific module distributions and 5 applications. The idea is that while every "config" command is different, 6 at least they're all named the same, and users always see "config" in the 7 list of standard commands. Also, this is a good place to put common 8 configure-like tasks: "try to compile this C code", or "figure out where 9 this header file lives". 10 """ 11 12 __revision__ = "$Id$" 13 14 import os 15 import re 16 17 from distutils.core import Command 18 from distutils.errors import DistutilsExecError 19 from distutils.sysconfig import customize_compiler 20 from distutils import log 21 22 LANG_EXT = {'c': '.c', 'c++': '.cxx'} 23 24 class config(Command): 25 26 description = "prepare to build" 27 28 user_options = [ 29 ('compiler=', None, 30 "specify the compiler type"), 31 ('cc=', None, 32 "specify the compiler executable"), 33 ('include-dirs=', 'I', 34 "list of directories to search for header files"), 35 ('define=', 'D', 36 "C preprocessor macros to define"), 37 ('undef=', 'U', 38 "C preprocessor macros to undefine"), 39 ('libraries=', 'l', 40 "external C libraries to link with"), 41 ('library-dirs=', 'L', 42 "directories to search for external C libraries"), 43 44 ('noisy', None, 45 "show every action (compile, link, run, ...) taken"), 46 ('dump-source', None, 47 "dump generated source files before attempting to compile them"), 48 ] 49 50 51 # The three standard command methods: since the "config" command 52 # does nothing by default, these are empty. 53 54 def initialize_options(self): 55 self.compiler = None 56 self.cc = None 57 self.include_dirs = None 58 self.libraries = None 59 self.library_dirs = None 60 61 # maximal output for now 62 self.noisy = 1 63 self.dump_source = 1 64 65 # list of temporary files generated along-the-way that we have 66 # to clean at some point 67 self.temp_files = [] 68 69 def finalize_options(self): 70 if self.include_dirs is None: 71 self.include_dirs = self.distribution.include_dirs or [] 72 elif isinstance(self.include_dirs, str): 73 self.include_dirs = self.include_dirs.split(os.pathsep) 74 75 if self.libraries is None: 76 self.libraries = [] 77 elif isinstance(self.libraries, str): 78 self.libraries = [self.libraries] 79 80 if self.library_dirs is None: 81 self.library_dirs = [] 82 elif isinstance(self.library_dirs, str): 83 self.library_dirs = self.library_dirs.split(os.pathsep) 84 85 def run(self): 86 pass 87 88 89 # Utility methods for actual "config" commands. The interfaces are 90 # loosely based on Autoconf macros of similar names. Sub-classes 91 # may use these freely. 92 93 def _check_compiler(self): 94 """Check that 'self.compiler' really is a CCompiler object; 95 if not, make it one. 96 """ 97 # We do this late, and only on-demand, because this is an expensive 98 # import. 99 from distutils.ccompiler import CCompiler, new_compiler 100 if not isinstance(self.compiler, CCompiler): 101 self.compiler = new_compiler(compiler=self.compiler, 102 dry_run=self.dry_run, force=1) 103 customize_compiler(self.compiler) 104 if self.include_dirs: 105 self.compiler.set_include_dirs(self.include_dirs) 106 if self.libraries: 107 self.compiler.set_libraries(self.libraries) 108 if self.library_dirs: 109 self.compiler.set_library_dirs(self.library_dirs) 110 111 112 def _gen_temp_sourcefile(self, body, headers, lang): 113 filename = "_configtest" + LANG_EXT[lang] 114 file = open(filename, "w") 115 if headers: 116 for header in headers: 117 file.write("#include <%s>\n" % header) 118 file.write("\n") 119 file.write(body) 120 if body[-1] != "\n": 121 file.write("\n") 122 file.close() 123 return filename 124 125 def _preprocess(self, body, headers, include_dirs, lang): 126 src = self._gen_temp_sourcefile(body, headers, lang) 127 out = "_configtest.i" 128 self.temp_files.extend([src, out]) 129 self.compiler.preprocess(src, out, include_dirs=include_dirs) 130 return (src, out) 131 132 def _compile(self, body, headers, include_dirs, lang): 133 src = self._gen_temp_sourcefile(body, headers, lang) 134 if self.dump_source: 135 dump_file(src, "compiling '%s':" % src) 136 (obj,) = self.compiler.object_filenames([src]) 137 self.temp_files.extend([src, obj]) 138 self.compiler.compile([src], include_dirs=include_dirs) 139 return (src, obj) 140 141 def _link(self, body, headers, include_dirs, libraries, library_dirs, 142 lang): 143 (src, obj) = self._compile(body, headers, include_dirs, lang) 144 prog = os.path.splitext(os.path.basename(src))[0] 145 self.compiler.link_executable([obj], prog, 146 libraries=libraries, 147 library_dirs=library_dirs, 148 target_lang=lang) 149 150 if self.compiler.exe_extension is not None: 151 prog = prog + self.compiler.exe_extension 152 self.temp_files.append(prog) 153 154 return (src, obj, prog) 155 156 def _clean(self, *filenames): 157 if not filenames: 158 filenames = self.temp_files 159 self.temp_files = [] 160 log.info("removing: %s", ' '.join(filenames)) 161 for filename in filenames: 162 try: 163 os.remove(filename) 164 except OSError: 165 pass 166 167 168 # XXX these ignore the dry-run flag: what to do, what to do? even if 169 # you want a dry-run build, you still need some sort of configuration 170 # info. My inclination is to make it up to the real config command to 171 # consult 'dry_run', and assume a default (minimal) configuration if 172 # true. The problem with trying to do it here is that you'd have to 173 # return either true or false from all the 'try' methods, neither of 174 # which is correct. 175 176 # XXX need access to the header search path and maybe default macros. 177 178 def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): 179 """Construct a source file from 'body' (a string containing lines 180 of C/C++ code) and 'headers' (a list of header files to include) 181 and run it through the preprocessor. Return true if the 182 preprocessor succeeded, false if there were any errors. 183 ('body' probably isn't of much use, but what the heck.) 184 """ 185 from distutils.ccompiler import CompileError 186 self._check_compiler() 187 ok = 1 188 try: 189 self._preprocess(body, headers, include_dirs, lang) 190 except CompileError: 191 ok = 0 192 193 self._clean() 194 return ok 195 196 def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, 197 lang="c"): 198 """Construct a source file (just like 'try_cpp()'), run it through 199 the preprocessor, and return true if any line of the output matches 200 'pattern'. 'pattern' should either be a compiled regex object or a 201 string containing a regex. If both 'body' and 'headers' are None, 202 preprocesses an empty file -- which can be useful to determine the 203 symbols the preprocessor and compiler set by default. 204 """ 205 self._check_compiler() 206 src, out = self._preprocess(body, headers, include_dirs, lang) 207 208 if isinstance(pattern, str): 209 pattern = re.compile(pattern) 210 211 file = open(out) 212 match = 0 213 while 1: 214 line = file.readline() 215 if line == '': 216 break 217 if pattern.search(line): 218 match = 1 219 break 220 221 file.close() 222 self._clean() 223 return match 224 225 def try_compile(self, body, headers=None, include_dirs=None, lang="c"): 226 """Try to compile a source file built from 'body' and 'headers'. 227 Return true on success, false otherwise. 228 """ 229 from distutils.ccompiler import CompileError 230 self._check_compiler() 231 try: 232 self._compile(body, headers, include_dirs, lang) 233 ok = 1 234 except CompileError: 235 ok = 0 236 237 log.info(ok and "success!" or "failure.") 238 self._clean() 239 return ok 240 241 def try_link(self, body, headers=None, include_dirs=None, libraries=None, 242 library_dirs=None, lang="c"): 243 """Try to compile and link a source file, built from 'body' and 244 'headers', to executable form. Return true on success, false 245 otherwise. 246 """ 247 from distutils.ccompiler import CompileError, LinkError 248 self._check_compiler() 249 try: 250 self._link(body, headers, include_dirs, 251 libraries, library_dirs, lang) 252 ok = 1 253 except (CompileError, LinkError): 254 ok = 0 255 256 log.info(ok and "success!" or "failure.") 257 self._clean() 258 return ok 259 260 def try_run(self, body, headers=None, include_dirs=None, libraries=None, 261 library_dirs=None, lang="c"): 262 """Try to compile, link to an executable, and run a program 263 built from 'body' and 'headers'. Return true on success, false 264 otherwise. 265 """ 266 from distutils.ccompiler import CompileError, LinkError 267 self._check_compiler() 268 try: 269 src, obj, exe = self._link(body, headers, include_dirs, 270 libraries, library_dirs, lang) 271 self.spawn([exe]) 272 ok = 1 273 except (CompileError, LinkError, DistutilsExecError): 274 ok = 0 275 276 log.info(ok and "success!" or "failure.") 277 self._clean() 278 return ok 279 280 281 # -- High-level methods -------------------------------------------- 282 # (these are the ones that are actually likely to be useful 283 # when implementing a real-world config command!) 284 285 def check_func(self, func, headers=None, include_dirs=None, 286 libraries=None, library_dirs=None, decl=0, call=0): 287 288 """Determine if function 'func' is available by constructing a 289 source file that refers to 'func', and compiles and links it. 290 If everything succeeds, returns true; otherwise returns false. 291 292 The constructed source file starts out by including the header 293 files listed in 'headers'. If 'decl' is true, it then declares 294 'func' (as "int func()"); you probably shouldn't supply 'headers' 295 and set 'decl' true in the same call, or you might get errors about 296 a conflicting declarations for 'func'. Finally, the constructed 297 'main()' function either references 'func' or (if 'call' is true) 298 calls it. 'libraries' and 'library_dirs' are used when 299 linking. 300 """ 301 302 self._check_compiler() 303 body = [] 304 if decl: 305 body.append("int %s ();" % func) 306 body.append("int main () {") 307 if call: 308 body.append(" %s();" % func) 309 else: 310 body.append(" %s;" % func) 311 body.append("}") 312 body = "\n".join(body) + "\n" 313 314 return self.try_link(body, headers, include_dirs, 315 libraries, library_dirs) 316 317 # check_func () 318 319 def check_lib(self, library, library_dirs=None, headers=None, 320 include_dirs=None, other_libraries=[]): 321 """Determine if 'library' is available to be linked against, 322 without actually checking that any particular symbols are provided 323 by it. 'headers' will be used in constructing the source file to 324 be compiled, but the only effect of this is to check if all the 325 header files listed are available. Any libraries listed in 326 'other_libraries' will be included in the link, in case 'library' 327 has symbols that depend on other libraries. 328 """ 329 self._check_compiler() 330 return self.try_link("int main (void) { }", 331 headers, include_dirs, 332 [library]+other_libraries, library_dirs) 333 334 def check_header(self, header, include_dirs=None, library_dirs=None, 335 lang="c"): 336 """Determine if the system header file named by 'header_file' 337 exists and can be found by the preprocessor; return true if so, 338 false otherwise. 339 """ 340 return self.try_cpp(body="/* No body */", headers=[header], 341 include_dirs=include_dirs) 342 343 344 def dump_file(filename, head=None): 345 """Dumps a file content into log.info. 346 347 If head is not None, will be dumped before the file content. 348 """ 349 if head is None: 350 log.info('%s' % filename) 351 else: 352 log.info(head) 353 file = open(filename) 354 try: 355 log.info(file.read()) 356 finally: 357 file.close() 358