PageRenderTime 487ms CodeModel.GetById 201ms app.highlight 127ms RepoModel.GetById 155ms app.codeStats 0ms

/Lib/distutils/command/config.py

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