Commit 59ac7091 authored by Greg Ward's avatar Greg Ward

Fleshed out and added a bunch of useful stuff, notably 'check_func()',

'try_cpp()', 'search_cpp()', and 'check_header()'.  This is enough that
the base config is actually useful for implementing a real config
command, specifically one for mxDateTime.
parent 71257c73
...@@ -13,7 +13,7 @@ this header file lives". ...@@ -13,7 +13,7 @@ this header file lives".
__revision__ = "$Id$" __revision__ = "$Id$"
import os, string import sys, os, string, re
from distutils.core import Command from distutils.core import Command
from distutils.errors import DistutilsExecError from distutils.errors import DistutilsExecError
...@@ -40,6 +40,11 @@ class config (Command): ...@@ -40,6 +40,11 @@ class config (Command):
"external C libraries to link with"), "external C libraries to link with"),
('library-dirs=', 'L', ('library-dirs=', 'L',
"directories to search for external C libraries"), "directories to search for external C libraries"),
('noisy', None,
"show every action (compile, link, run, ...) taken"),
('dump-source', None,
"dump generated source files before attempting to compile them"),
] ]
...@@ -55,6 +60,14 @@ class config (Command): ...@@ -55,6 +60,14 @@ class config (Command):
self.libraries = None self.libraries = None
self.library_dirs = None self.library_dirs = None
# maximal output for now
self.noisy = 1
self.dump_source = 1
# list of temporary files generated along-the-way that we have
# to clean at some point
self.temp_files = []
def finalize_options (self): def finalize_options (self):
pass pass
...@@ -75,7 +88,7 @@ class config (Command): ...@@ -75,7 +88,7 @@ class config (Command):
from distutils.ccompiler import CCompiler, new_compiler from distutils.ccompiler import CCompiler, new_compiler
if not isinstance(self.compiler, CCompiler): if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler (compiler=self.compiler, self.compiler = new_compiler (compiler=self.compiler,
verbose=self.verbose, # for now verbose=self.noisy,
dry_run=self.dry_run, dry_run=self.dry_run,
force=1) force=1)
if self.include_dirs: if self.include_dirs:
...@@ -86,25 +99,48 @@ class config (Command): ...@@ -86,25 +99,48 @@ class config (Command):
self.compiler.set_library_dirs(self.library_dirs) self.compiler.set_library_dirs(self.library_dirs)
def _gen_temp_sourcefile (self, body, lang): def _gen_temp_sourcefile (self, body, headers, lang):
filename = "_configtest" + LANG_EXT[lang] filename = "_configtest" + LANG_EXT[lang]
file = open(filename, "w") file = open(filename, "w")
if headers:
for header in headers:
file.write("#include <%s>\n" % header)
file.write("\n")
file.write(body) file.write(body)
if body[-1] != "\n":
file.write("\n")
file.close() file.close()
return filename return filename
def _compile (self, body, lang): def _preprocess (self, body, headers, lang):
src = self._gen_temp_sourcefile(body, lang) src = self._gen_temp_sourcefile(body, headers, lang)
(obj,) = self.compiler.compile([src]) out = "_configtest.i"
self.temp_files.extend([src, out])
self.compiler.preprocess(src, out)
return (src, out)
def _compile (self, body, headers, lang):
src = self._gen_temp_sourcefile(body, headers, lang)
if self.dump_source:
dump_file(src, "compiling '%s':" % src)
(obj,) = self.compiler.object_filenames([src])
self.temp_files.extend([src, obj])
self.compiler.compile([src])
return (src, obj) return (src, obj)
def _link (self, body, lang): def _link (self, body, headers, libraries, library_dirs, lang):
(src, obj) = self._compile(body, lang) (src, obj) = self._compile(body, headers, lang)
exe = os.path.splitext(os.path.basename(src))[0] prog = os.path.splitext(os.path.basename(src))[0]
self.compiler.link_executable([obj], exe) self.temp_files.append(prog) # XXX should be prog + exe_ext
return (src, obj, exe) self.compiler.link_executable([obj], prog,
libraries=libraries,
library_dirs=library_dirs)
return (src, obj, prog)
def _clean (self, *filenames): def _clean (self, *filenames):
if not filenames:
filenames = self.temp_files
self.temp_files = []
self.announce("removing: " + string.join(filenames)) self.announce("removing: " + string.join(filenames))
for filename in filenames: for filename in filenames:
try: try:
...@@ -113,10 +149,6 @@ class config (Command): ...@@ -113,10 +149,6 @@ class config (Command):
pass pass
# XXX no 'try_cpp()' or 'search_cpp()' since the CCompiler interface
# does not provide access to the preprocessor. This is an oversight
# that should be fixed.
# XXX these ignore the dry-run flag: what to do, what to do? even if # XXX these ignore the dry-run flag: what to do, what to do? even if
# you want a dry-run build, you still need some sort of configuration # you want a dry-run build, you still need some sort of configuration
# info. My inclination is to make it up to the real config command to # info. My inclination is to make it up to the real config command to
...@@ -125,56 +157,168 @@ class config (Command): ...@@ -125,56 +157,168 @@ class config (Command):
# return either true or false from all the 'try' methods, neither of # return either true or false from all the 'try' methods, neither of
# which is correct. # which is correct.
def try_compile (self, body, lang="c"): # XXX need access to the header search path and maybe default macros.
"""Try to compile a source file that consists of the text in 'body'
(a multi-line string). Return true on success, false def try_cpp (self, body=None, headers=None, lang="c"):
otherwise. """Construct a source file from 'body' (a string containing lines
of C/C++ code) and 'headers' (a list of header files to include)
and run it through the preprocessor. Return true if the
preprocessor succeeded, false if there were any errors.
('body' probably isn't of much use, but what the heck.)
""" """
from distutils.ccompiler import CompileError from distutils.ccompiler import CompileError
self._check_compiler() self._check_compiler()
ok = 1
try: try:
(src, obj) = self._compile(body, lang) self._preprocess(body, headers, lang)
except CompileError:
ok = 0
self._clean()
return ok
def search_cpp (self, pattern, body=None, headers=None, lang="c"):
"""Construct a source file (just like 'try_cpp()'), run it through
the preprocessor, and return true if any line of the output matches
'pattern'. 'pattern' should either be a compiled regex object or a
string containing a regex. If both 'body' and 'headers' are None,
preprocesses an empty file -- which can be useful to determine the
symbols the preprocessor and compiler set by default.
"""
self._check_compiler()
(src, out) = self._preprocess(body, headers, lang)
if type(pattern) is StringType:
pattern = re.compile(pattern)
file = open(out)
match = 0
while 1:
line = file.readline()
if line == '':
break
if pattern.search(pattern):
match = 1
break
file.close()
self._clean()
return match
def try_compile (self, body, headers=None, lang="c"):
"""Try to compile a source file built from 'body' and 'headers'.
Return true on success, false otherwise.
"""
from distutils.ccompiler import CompileError
self._check_compiler()
try:
self._compile(body, headers, lang)
ok = 1 ok = 1
except CompileError: except CompileError:
ok = 0 ok = 0
self.announce(ok and "success!" or "failure.") self.announce(ok and "success!" or "failure.")
self._clean(src, obj) self._clean()
return ok return ok
def try_link (self, body, lang="c"): def try_link (self,
"""Try to compile and link a source file (to an executable) that body, headers=None,
consists of the text in 'body' (a multi-line string). Return true libraries=None, library_dirs=None,
on success, false otherwise. lang="c"):
"""Try to compile and link a source file, built from 'body' and
'headers', to executable form. Return true on success, false
otherwise.
""" """
from distutils.ccompiler import CompileError, LinkError from distutils.ccompiler import CompileError, LinkError
self._check_compiler() self._check_compiler()
try: try:
(src, obj, exe) = self._link(body, lang) self._link(body, headers, libraries, library_dirs, lang)
ok = 1 ok = 1
except (CompileError, LinkError): except (CompileError, LinkError):
ok = 0 ok = 0
self.announce(ok and "success!" or "failure.") self.announce(ok and "success!" or "failure.")
self._clean(src, obj, exe) self._clean()
return ok return ok
def try_run (self, body, lang="c"): def try_run (self,
"""Try to compile, link to an executable, and run a program that body, headers=None,
consists of the text in 'body'. Return true on success, false libraries=None, library_dirs=None,
lang="c"):
"""Try to compile, link to an executable, and run a program
built from 'body' and 'headers'. Return true on success, false
otherwise. otherwise.
""" """
from distutils.ccompiler import CompileError, LinkError from distutils.ccompiler import CompileError, LinkError
self._check_compiler() self._check_compiler()
try: try:
(src, obj, exe) = self._link(body, lang) self._link(body, headers, libraries, library_dirs, lang)
self.spawn([exe]) self.spawn([exe])
ok = 1 ok = 1
except (CompileError, LinkError, DistutilsExecError): except (CompileError, LinkError, DistutilsExecError):
ok = 0 ok = 0
self.announce(ok and "success!" or "failure.") self.announce(ok and "success!" or "failure.")
self._clean(src, obj, exe) self._clean()
return ok return ok
# -- High-level methods --------------------------------------------
# (these are the ones that are actually likely to be useful
# when implementing a real-world config command!)
def check_func (self, func, headers=None,
libraries=None, library_dirs=None,
decl=0, call=0):
"""Determine if function 'func' is available by constructing a
source file that refers to 'func', and compiles and links it.
If everything succeeds, returns true; otherwise returns false.
The constructed source file starts out by including the header
files listed in 'headers'. If 'decl' is true, it then declares
'func' (as "int func()"); you probably shouldn't supply 'headers'
and set 'decl' true in the same call, or you might get errors about
a conflicting declarations for 'func'. Finally, the constructed
'main()' function either references 'func' or (if 'call' is true)
calls it. 'libraries' and 'library_dirs' are used when
linking.
"""
self._check_compiler()
body = []
if decl:
body.append("int %s ();" % func)
body.append("int main () {")
if call:
body.append(" %s();" % func)
else:
body.append(" %s;" % func)
body.append("}")
body = string.join(body, "\n") + "\n"
return self.try_link(body, headers, libraries, library_dirs)
# check_func ()
def check_header (self, header, lang="c"):
"""Determine if the system header file named by 'header_file'
exists and can be found by the preprocessor; return true if so,
false otherwise.
"""
return self.try_cpp(headers=[header])
# class config # class config
def dump_file (filename, head=None):
if head is None:
print filename + ":"
else:
print head
file = open(filename)
sys.stdout.write(file.read())
file.close()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment