summaryrefslogtreecommitdiffstats
path: root/Lib/distutils/command/config.py
diff options
context:
space:
mode:
authorGreg Ward <gward@python.net>2000-06-21 03:00:50 (GMT)
committerGreg Ward <gward@python.net>2000-06-21 03:00:50 (GMT)
commit59ac7091a7355233852a7cd34e0e0297d1eac3ea (patch)
tree68a4b0bf232dfac1e39055237b6eaa99127626e7 /Lib/distutils/command/config.py
parent71257c73f828389c020d4f3a3e3e52f6846d6f29 (diff)
downloadcpython-59ac7091a7355233852a7cd34e0e0297d1eac3ea.zip
cpython-59ac7091a7355233852a7cd34e0e0297d1eac3ea.tar.gz
cpython-59ac7091a7355233852a7cd34e0e0297d1eac3ea.tar.bz2
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.
Diffstat (limited to 'Lib/distutils/command/config.py')
-rw-r--r--Lib/distutils/command/config.py208
1 files changed, 176 insertions, 32 deletions
diff --git a/Lib/distutils/command/config.py b/Lib/distutils/command/config.py
index cad75bc..570f71a 100644
--- a/Lib/distutils/command/config.py
+++ b/Lib/distutils/command/config.py
@@ -13,7 +13,7 @@ this header file lives".
__revision__ = "$Id$"
-import os, string
+import sys, os, string, re
from distutils.core import Command
from distutils.errors import DistutilsExecError
@@ -40,6 +40,11 @@ class config (Command):
"external C libraries to link with"),
('library-dirs=', 'L',
"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):
self.libraries = 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):
pass
@@ -75,7 +88,7 @@ class config (Command):
from distutils.ccompiler import CCompiler, new_compiler
if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler (compiler=self.compiler,
- verbose=self.verbose, # for now
+ verbose=self.noisy,
dry_run=self.dry_run,
force=1)
if self.include_dirs:
@@ -86,25 +99,48 @@ class config (Command):
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]
file = open(filename, "w")
+ if headers:
+ for header in headers:
+ file.write("#include <%s>\n" % header)
+ file.write("\n")
file.write(body)
+ if body[-1] != "\n":
+ file.write("\n")
file.close()
return filename
- def _compile (self, body, lang):
- src = self._gen_temp_sourcefile(body, lang)
- (obj,) = self.compiler.compile([src])
+ def _preprocess (self, body, headers, lang):
+ src = self._gen_temp_sourcefile(body, headers, lang)
+ 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)
- def _link (self, body, lang):
- (src, obj) = self._compile(body, lang)
- exe = os.path.splitext(os.path.basename(src))[0]
- self.compiler.link_executable([obj], exe)
- return (src, obj, exe)
+ def _link (self, body, headers, libraries, library_dirs, lang):
+ (src, obj) = self._compile(body, headers, lang)
+ prog = os.path.splitext(os.path.basename(src))[0]
+ self.temp_files.append(prog) # XXX should be prog + exe_ext
+ self.compiler.link_executable([obj], prog,
+ libraries=libraries,
+ library_dirs=library_dirs)
+ return (src, obj, prog)
def _clean (self, *filenames):
+ if not filenames:
+ filenames = self.temp_files
+ self.temp_files = []
self.announce("removing: " + string.join(filenames))
for filename in filenames:
try:
@@ -113,10 +149,6 @@ class config (Command):
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
# 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
@@ -125,56 +157,168 @@ class config (Command):
# return either true or false from all the 'try' methods, neither of
# which is correct.
- def try_compile (self, body, lang="c"):
- """Try to compile a source file that consists of the text in 'body'
- (a multi-line string). Return true on success, false
- otherwise.
+ # XXX need access to the header search path and maybe default macros.
+
+ def try_cpp (self, body=None, headers=None, lang="c"):
+ """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
self._check_compiler()
+ ok = 1
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
except CompileError:
ok = 0
self.announce(ok and "success!" or "failure.")
- self._clean(src, obj)
+ self._clean()
return ok
- def try_link (self, body, lang="c"):
- """Try to compile and link a source file (to an executable) that
- consists of the text in 'body' (a multi-line string). Return true
- on success, false otherwise.
+ def try_link (self,
+ body, headers=None,
+ libraries=None, library_dirs=None,
+ 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
self._check_compiler()
try:
- (src, obj, exe) = self._link(body, lang)
+ self._link(body, headers, libraries, library_dirs, lang)
ok = 1
except (CompileError, LinkError):
ok = 0
self.announce(ok and "success!" or "failure.")
- self._clean(src, obj, exe)
+ self._clean()
return ok
- def try_run (self, body, lang="c"):
- """Try to compile, link to an executable, and run a program that
- consists of the text in 'body'. Return true on success, false
+ def try_run (self,
+ body, headers=None,
+ 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.
"""
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
- (src, obj, exe) = self._link(body, lang)
+ self._link(body, headers, libraries, library_dirs, lang)
self.spawn([exe])
ok = 1
except (CompileError, LinkError, DistutilsExecError):
ok = 0
self.announce(ok and "success!" or "failure.")
- self._clean(src, obj, exe)
+ self._clean()
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
+
+
+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()