First crack at the Distutils "config" command. Unlike other commands, this

one doesn't *do* anything by default; it's just there as a conduit for data
(eg. include dirs, libraries) from the user to the "build" commands.
However, it provides a couple of Autoconf-ish methods ('try_compile()',
'try_link()', 'try_run()') that derived, per-distribution "config" commands
can use to poke around the target system and see what's available.

Initial experimenst with mxDateTime indicate that higher-level methods are
necessary: analogs of Autoconf's AC_CHECK_HEADER, AC_CHECK_LIB will be
needed too (and that's just to probe the C/C++ system: how to probe the
Python system is wide open, and someday we'll have to worry about probing a
Java system too).
This commit is contained in:
Greg Ward 2000-06-06 02:57:07 +00:00
parent 43955c9a02
commit 28a5f44cca

View File

@ -0,0 +1,180 @@
"""distutils.command.config
Implements the Distutils 'config' command, a (mostly) empty command class
that exists mainly to be sub-classed by specific module distributions and
applications. The idea is that while every "config" command is different,
at least they're all named the same, and users always see "config" in the
list of standard commands. Also, this is a good place to put common
configure-like tasks: "try to compile this C code", or "figure out where
this header file lives".
"""
# created 2000/05/29, Greg Ward
__revision__ = "$Id$"
import os, string
from distutils.core import Command
from distutils.errors import DistutilsExecError
LANG_EXT = {'c': '.c',
'c++': '.cxx'}
class config (Command):
description = "prepare to build"
user_options = [
('compiler=', None,
"specify the compiler type"),
('cc=', None,
"specify the compiler executable"),
('include-dirs=', 'I',
"list of directories to search for header files"),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libraries=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries"),
]
# The three standard command methods: since the "config" command
# does nothing by default, these are empty.
def initialize_options (self):
self.compiler = None
self.cc = None
self.include_dirs = None
#self.define = None
#self.undef = None
self.libraries = None
self.library_dirs = None
def finalize_options (self):
pass
def run (self):
pass
# Utility methods for actual "config" commands. The interfaces are
# loosely based on Autoconf macros of similar names. Sub-classes
# may use these freely.
def _check_compiler (self):
"""Check that 'self.compiler' really is a CCompiler object;
if not, make it one.
"""
# We do this late, and only on-demand, because this is an expensive
# import.
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
dry_run=self.dry_run,
force=1)
if self.include_dirs:
self.compiler.set_include_dirs(self.include_dirs)
if self.libraries:
self.compiler.set_libraries(self.libraries)
if self.library_dirs:
self.compiler.set_library_dirs(self.library_dirs)
def _gen_temp_sourcefile (self, body, lang):
filename = "_configtest" + LANG_EXT[lang]
file = open(filename, "w")
file.write(body)
file.close()
return filename
def _compile (self, body, lang):
src = self._gen_temp_sourcefile(body, lang)
(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 _clean (self, *filenames):
self.announce("removing: " + string.join(filenames))
for filename in filenames:
try:
os.remove(filename)
except OSError:
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
# consult 'dry_run', and assume a default (minimal) configuration if
# true. The problem with trying to do it here is that you'd have to
# 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.
"""
from distutils.ccompiler import CompileError
self._check_compiler()
try:
(src, obj) = self._compile(body, lang)
ok = 1
except CompileError:
ok = 0
self.announce(ok and "success!" or "failure.")
self._clean(src, obj)
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.
"""
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
(src, obj, exe) = self._link(body, lang)
ok = 1
except (CompileError, LinkError):
ok = 0
self.announce(ok and "success!" or "failure.")
self._clean(src, obj, exe)
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
otherwise.
"""
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
(src, obj, exe) = self._link(body, lang)
self.spawn([exe])
ok = 1
except (CompileError, LinkError, DistutilsExecError):
ok = 0
self.announce(ok and "success!" or "failure.")
self._clean(src, obj, exe)
return ok
# class config