Home | History | Annotate | Download | only in command
      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