]> granicus.if.org Git - python/commitdiff
First crack at the Distutils "config" command. Unlike other commands, this
authorGreg Ward <gward@python.net>
Tue, 6 Jun 2000 02:57:07 +0000 (02:57 +0000)
committerGreg Ward <gward@python.net>
Tue, 6 Jun 2000 02:57:07 +0000 (02:57 +0000)
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).

Lib/distutils/command/config.py [new file with mode: 0644]

diff --git a/Lib/distutils/command/config.py b/Lib/distutils/command/config.py
new file mode 100644 (file)
index 0000000..cad75bc
--- /dev/null
@@ -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