Commits

Tarek Ziadé committed 657a8ae

Merged revisions 73336 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
r73336 | tarek.ziade | 2009-06-10 20:49:50 +0200 (Wed, 10 Jun 2009) | 1 line

Distutils: started code cleanup and test coverage for cygwinccompiler
........

Comments (0)

Files changed (2)

Lib/distutils/cygwinccompiler.py

 
 __revision__ = "$Id$"
 
-import os,sys,copy
+import os
+import sys
+import copy
+from subprocess import Popen, PIPE
+import re
+
 from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 from distutils.unixccompiler import UnixCCompiler
 from distutils.file_util import write_file
 from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
 from distutils import log
+from distutils.version import LooseVersion
+from distutils.spawn import find_executable
 
 def get_msvcr():
     """Include the appropriate MSVC runtime library if Python was built
 CONFIG_H_UNCERTAIN = "uncertain"
 
 def check_config_h():
+    """Check if the current Python installation appears amenable to building
+    extensions with GCC.
 
-    """Check if the current Python installation (specifically, pyconfig.h)
-    appears amenable to building extensions with GCC.  Returns a tuple
-    (status, details), where 'status' is one of the following constants:
-      CONFIG_H_OK
-        all is well, go ahead and compile
-      CONFIG_H_NOTOK
-        doesn't look good
-      CONFIG_H_UNCERTAIN
-        not sure -- unable to read pyconfig.h
+    Returns a tuple (status, details), where 'status' is one of the following
+    constants:
+
+    - CONFIG_H_OK: all is well, go ahead and compile
+    - CONFIG_H_NOTOK: doesn't look good
+    - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
+
     'details' is a human-readable string explaining the situation.
 
     Note there are two ways to conclude "OK": either 'sys.version' contains
     # "pyconfig.h" check -- should probably be renamed...
 
     from distutils import sysconfig
-    # if sys.version contains GCC then python was compiled with
-    # GCC, and the pyconfig.h file should be OK
-    if sys.version.find("GCC") >= 0:
-        return (CONFIG_H_OK, "sys.version mentions 'GCC'")
 
+    # if sys.version contains GCC then python was compiled with GCC, and the
+    # pyconfig.h file should be OK
+    if "GCC" in sys.version:
+        return CONFIG_H_OK, "sys.version mentions 'GCC'"
+
+    # let's see if __GNUC__ is mentioned in python.h
     fn = sysconfig.get_config_h_filename()
     try:
-        # It would probably better to read single lines to search.
-        # But we do this only once, and it is fast enough
-        f = open(fn)
-        s = f.read()
-        f.close()
-
+        with open(fn) as config_h:
+            if "__GNUC__" in config_h.read():
+                return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
+            else:
+                return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
     except IOError as exc:
-        # if we can't read this file, we cannot say it is wrong
-        # the compiler will complain later about this file as missing
         return (CONFIG_H_UNCERTAIN,
                 "couldn't read '%s': %s" % (fn, exc.strerror))
 
-    else:
-        # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
-        if s.find("__GNUC__") >= 0:
-            return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
-        else:
-            return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
+RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
 
+def _find_exe_version(cmd):
+    """Find the version of an executable by running `cmd` in the shell.
 
+    If the command is not found, or the output does not match
+    `RE_VERSION`, returns None.
+    """
+    executable = cmd.split()[0]
+    if find_executable(executable) is None:
+        return None
+    out = Popen(cmd, shell=True, stdout=PIPE).stdout
+    try:
+        out_string = out.read()
+    finally:
+        out.close()
+    result = RE_VERSION.search(out_string)
+    if result is None:
+        return None
+    return LooseVersion(result.group(1))
 
 def get_versions():
     """ Try to find out the versions of gcc, ld and dllwrap.
-        If not possible it returns None for it.
+
+    If not possible it returns None for it.
     """
-    from distutils.version import LooseVersion
-    from distutils.spawn import find_executable
-    import re
-
-    gcc_exe = find_executable('gcc')
-    if gcc_exe:
-        out = os.popen(gcc_exe + ' -dumpversion','r')
-        out_string = out.read()
-        out.close()
-        result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII)
-        if result:
-            gcc_version = LooseVersion(result.group(1))
-        else:
-            gcc_version = None
-    else:
-        gcc_version = None
-    ld_exe = find_executable('ld')
-    if ld_exe:
-        out = os.popen(ld_exe + ' -v','r')
-        out_string = out.read()
-        out.close()
-        result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII)
-        if result:
-            ld_version = LooseVersion(result.group(1))
-        else:
-            ld_version = None
-    else:
-        ld_version = None
-    dllwrap_exe = find_executable('dllwrap')
-    if dllwrap_exe:
-        out = os.popen(dllwrap_exe + ' --version','r')
-        out_string = out.read()
-        out.close()
-        result = re.search(' (\d+\.\d+(\.\d+)*)', out_string, re.ASCII)
-        if result:
-            dllwrap_version = LooseVersion(result.group(1))
-        else:
-            dllwrap_version = None
-    else:
-        dllwrap_version = None
-    return (gcc_version, ld_version, dllwrap_version)
+    commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
+    return tuple([_find_exe_version(cmd) for cmd in commands])

Lib/distutils/tests/test_cygwinccompiler.py

+"""Tests for distutils.cygwinccompiler."""
+import unittest
+import sys
+import os
+from io import StringIO
+import subprocess
+
+from distutils import cygwinccompiler
+from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h,
+                                       CONFIG_H_OK, CONFIG_H_NOTOK,
+                                       CONFIG_H_UNCERTAIN, get_versions)
+from distutils.tests import support
+
+class FakePopen(object):
+    test_class = None
+
+    def __init__(self, cmd, shell, stdout):
+        self.cmd = cmd.split()[0]
+        exes = self.test_class._exes
+        if self.cmd in exes:
+            self.stdout = StringIO(exes[self.cmd])
+        else:
+            self.stdout = os.popen(cmd, 'r')
+
+
+class CygwinCCompilerTestCase(support.TempdirManager,
+                              unittest.TestCase):
+
+    def setUp(self):
+        super(CygwinCCompilerTestCase, self).setUp()
+        self.version = sys.version
+        self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+        from distutils import sysconfig
+        self.old_get_config_h_filename = sysconfig.get_config_h_filename
+        sysconfig.get_config_h_filename = self._get_config_h_filename
+        self.old_find_executable = cygwinccompiler.find_executable
+        cygwinccompiler.find_executable = self._find_executable
+        self._exes = {}
+        self.old_popen = cygwinccompiler.Popen
+        FakePopen.test_class = self
+        cygwinccompiler.Popen = FakePopen
+
+    def tearDown(self):
+        sys.version = self.version
+        from distutils import sysconfig
+        sysconfig.get_config_h_filename = self.old_get_config_h_filename
+        cygwinccompiler.find_executable = self.old_find_executable
+        cygwinccompiler.Popen = self.old_popen
+        super(CygwinCCompilerTestCase, self).tearDown()
+
+    def _get_config_h_filename(self):
+        return self.python_h
+
+    def _find_executable(self, name):
+        if name in self._exes:
+            return name
+        return None
+
+    def test_check_config_h(self):
+
+        # check_config_h looks for "GCC" in sys.version first
+        # returns CONFIG_H_OK if found
+        sys.version = ('2.6.1 (r261:67515, Dec  6 2008, 16:42:21) \n[GCC '
+                       '4.0.1 (Apple Computer, Inc. build 5370)]')
+
+        self.assertEquals(check_config_h()[0], CONFIG_H_OK)
+
+        # then it tries to see if it can find "__GNUC__" in pyconfig.h
+        sys.version = 'something without the *CC word'
+
+        # if the file doesn't exist it returns  CONFIG_H_UNCERTAIN
+        self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN)
+
+        # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
+        self.write_file(self.python_h, 'xxx')
+        self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK)
+
+        # and CONFIG_H_OK if __GNUC__ is found
+        self.write_file(self.python_h, 'xxx __GNUC__ xxx')
+        self.assertEquals(check_config_h()[0], CONFIG_H_OK)
+
+    def test_get_versions(self):
+
+        # get_versions calls distutils.spawn.find_executable on
+        # 'gcc', 'ld' and 'dllwrap'
+        self.assertEquals(get_versions(), (None, None, None))
+
+        # Let's fake we have 'gcc' and it returns '3.4.5'
+        self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
+        res = get_versions()
+        self.assertEquals(str(res[0]), '3.4.5')
+
+        # and let's see what happens when the version
+        # doesn't match the regular expression
+        # (\d+\.\d+(\.\d+)*)
+        self._exes['gcc'] = 'very strange output'
+        res = get_versions()
+        self.assertEquals(res[0], None)
+
+        # same thing for ld
+        self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+        res = get_versions()
+        self.assertEquals(str(res[1]), '2.17.50')
+        self._exes['ld'] = '@(#)PROGRAM:ld  PROJECT:ld64-77'
+        res = get_versions()
+        self.assertEquals(res[1], None)
+
+        # and dllwrap
+        self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
+        res = get_versions()
+        self.assertEquals(str(res[2]), '2.17.50')
+        self._exes['dllwrap'] = 'Cheese Wrap'
+        res = get_versions()
+        self.assertEquals(res[2], None)
+
+def test_suite():
+    return unittest.makeSuite(CygwinCCompilerTestCase)
+
+if __name__ == '__main__':
+    test_support.run_unittest(test_suite())