mirror of
https://github.com/python/cpython.git
synced 2024-11-27 03:45:08 +08:00
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:
parent
43955c9a02
commit
28a5f44cca
180
Lib/distutils/command/config.py
Normal file
180
Lib/distutils/command/config.py
Normal 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
|
Loading…
Reference in New Issue
Block a user