1"""distutils.command.config 2 3Implements the Distutils 'config' command, a (mostly) empty command class 4that exists mainly to be sub-classed by specific module distributions and 5applications. The idea is that while every "config" command is different, 6at least they're all named the same, and users always see "config" in the 7list of standard commands. Also, this is a good place to put common 8configure-like tasks: "try to compile this C code", or "figure out where 9this header file lives". 10""" 11 12import os, re 13 14from distutils.core import Command 15from distutils.errors import DistutilsExecError 16from distutils.sysconfig import customize_compiler 17from distutils import log 18 19LANG_EXT = {"c": ".c", "c++": ".cxx"} 20 21class 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 with open(filename, "w") as file: 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 return filename 118 119 def _preprocess(self, body, headers, include_dirs, lang): 120 src = self._gen_temp_sourcefile(body, headers, lang) 121 out = "_configtest.i" 122 self.temp_files.extend([src, out]) 123 self.compiler.preprocess(src, out, include_dirs=include_dirs) 124 return (src, out) 125 126 def _compile(self, body, headers, include_dirs, lang): 127 src = self._gen_temp_sourcefile(body, headers, lang) 128 if self.dump_source: 129 dump_file(src, "compiling '%s':" % src) 130 (obj,) = self.compiler.object_filenames([src]) 131 self.temp_files.extend([src, obj]) 132 self.compiler.compile([src], include_dirs=include_dirs) 133 return (src, obj) 134 135 def _link(self, body, headers, include_dirs, libraries, library_dirs, 136 lang): 137 (src, obj) = self._compile(body, headers, include_dirs, lang) 138 prog = os.path.splitext(os.path.basename(src))[0] 139 self.compiler.link_executable([obj], prog, 140 libraries=libraries, 141 library_dirs=library_dirs, 142 target_lang=lang) 143 144 if self.compiler.exe_extension is not None: 145 prog = prog + self.compiler.exe_extension 146 self.temp_files.append(prog) 147 148 return (src, obj, prog) 149 150 def _clean(self, *filenames): 151 if not filenames: 152 filenames = self.temp_files 153 self.temp_files = [] 154 log.info("removing: %s", ' '.join(filenames)) 155 for filename in filenames: 156 try: 157 os.remove(filename) 158 except OSError: 159 pass 160 161 162 # XXX these ignore the dry-run flag: what to do, what to do? even if 163 # you want a dry-run build, you still need some sort of configuration 164 # info. My inclination is to make it up to the real config command to 165 # consult 'dry_run', and assume a default (minimal) configuration if 166 # true. The problem with trying to do it here is that you'd have to 167 # return either true or false from all the 'try' methods, neither of 168 # which is correct. 169 170 # XXX need access to the header search path and maybe default macros. 171 172 def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): 173 """Construct a source file from 'body' (a string containing lines 174 of C/C++ code) and 'headers' (a list of header files to include) 175 and run it through the preprocessor. Return true if the 176 preprocessor succeeded, false if there were any errors. 177 ('body' probably isn't of much use, but what the heck.) 178 """ 179 from distutils.ccompiler import CompileError 180 self._check_compiler() 181 ok = True 182 try: 183 self._preprocess(body, headers, include_dirs, lang) 184 except CompileError: 185 ok = False 186 187 self._clean() 188 return ok 189 190 def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, 191 lang="c"): 192 """Construct a source file (just like 'try_cpp()'), run it through 193 the preprocessor, and return true if any line of the output matches 194 'pattern'. 'pattern' should either be a compiled regex object or a 195 string containing a regex. If both 'body' and 'headers' are None, 196 preprocesses an empty file -- which can be useful to determine the 197 symbols the preprocessor and compiler set by default. 198 """ 199 self._check_compiler() 200 src, out = self._preprocess(body, headers, include_dirs, lang) 201 202 if isinstance(pattern, str): 203 pattern = re.compile(pattern) 204 205 with open(out) as file: 206 match = False 207 while True: 208 line = file.readline() 209 if line == '': 210 break 211 if pattern.search(line): 212 match = True 213 break 214 215 self._clean() 216 return match 217 218 def try_compile(self, body, headers=None, include_dirs=None, lang="c"): 219 """Try to compile a source file built from 'body' and 'headers'. 220 Return true on success, false otherwise. 221 """ 222 from distutils.ccompiler import CompileError 223 self._check_compiler() 224 try: 225 self._compile(body, headers, include_dirs, lang) 226 ok = True 227 except CompileError: 228 ok = False 229 230 log.info(ok and "success!" or "failure.") 231 self._clean() 232 return ok 233 234 def try_link(self, body, headers=None, include_dirs=None, libraries=None, 235 library_dirs=None, lang="c"): 236 """Try to compile and link a source file, built from 'body' and 237 'headers', to executable form. Return true on success, false 238 otherwise. 239 """ 240 from distutils.ccompiler import CompileError, LinkError 241 self._check_compiler() 242 try: 243 self._link(body, headers, include_dirs, 244 libraries, library_dirs, lang) 245 ok = True 246 except (CompileError, LinkError): 247 ok = False 248 249 log.info(ok and "success!" or "failure.") 250 self._clean() 251 return ok 252 253 def try_run(self, body, headers=None, include_dirs=None, libraries=None, 254 library_dirs=None, lang="c"): 255 """Try to compile, link to an executable, and run a program 256 built from 'body' and 'headers'. Return true on success, false 257 otherwise. 258 """ 259 from distutils.ccompiler import CompileError, LinkError 260 self._check_compiler() 261 try: 262 src, obj, exe = self._link(body, headers, include_dirs, 263 libraries, library_dirs, lang) 264 self.spawn([exe]) 265 ok = True 266 except (CompileError, LinkError, DistutilsExecError): 267 ok = False 268 269 log.info(ok and "success!" or "failure.") 270 self._clean() 271 return ok 272 273 274 # -- High-level methods -------------------------------------------- 275 # (these are the ones that are actually likely to be useful 276 # when implementing a real-world config command!) 277 278 def check_func(self, func, headers=None, include_dirs=None, 279 libraries=None, library_dirs=None, decl=0, call=0): 280 """Determine if function 'func' is available by constructing a 281 source file that refers to 'func', and compiles and links it. 282 If everything succeeds, returns true; otherwise returns false. 283 284 The constructed source file starts out by including the header 285 files listed in 'headers'. If 'decl' is true, it then declares 286 'func' (as "int func()"); you probably shouldn't supply 'headers' 287 and set 'decl' true in the same call, or you might get errors about 288 a conflicting declarations for 'func'. Finally, the constructed 289 'main()' function either references 'func' or (if 'call' is true) 290 calls it. 'libraries' and 'library_dirs' are used when 291 linking. 292 """ 293 self._check_compiler() 294 body = [] 295 if decl: 296 body.append("int %s ();" % func) 297 body.append("int main () {") 298 if call: 299 body.append(" %s();" % func) 300 else: 301 body.append(" %s;" % func) 302 body.append("}") 303 body = "\n".join(body) + "\n" 304 305 return self.try_link(body, headers, include_dirs, 306 libraries, library_dirs) 307 308 def check_lib(self, library, library_dirs=None, headers=None, 309 include_dirs=None, other_libraries=[]): 310 """Determine if 'library' is available to be linked against, 311 without actually checking that any particular symbols are provided 312 by it. 'headers' will be used in constructing the source file to 313 be compiled, but the only effect of this is to check if all the 314 header files listed are available. Any libraries listed in 315 'other_libraries' will be included in the link, in case 'library' 316 has symbols that depend on other libraries. 317 """ 318 self._check_compiler() 319 return self.try_link("int main (void) { }", headers, include_dirs, 320 [library] + other_libraries, library_dirs) 321 322 def check_header(self, header, include_dirs=None, library_dirs=None, 323 lang="c"): 324 """Determine if the system header file named by 'header_file' 325 exists and can be found by the preprocessor; return true if so, 326 false otherwise. 327 """ 328 return self.try_cpp(body="/* No body */", headers=[header], 329 include_dirs=include_dirs) 330 331def dump_file(filename, head=None): 332 """Dumps a file content into log.info. 333 334 If head is not None, will be dumped before the file content. 335 """ 336 if head is None: 337 log.info('%s', filename) 338 else: 339 log.info(head) 340 file = open(filename) 341 try: 342 log.info(file.read()) 343 finally: 344 file.close() 345