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