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 __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