#6466 refactored distutils duplicate get_versions() functions (used to get gcc/ld/dllwrap versions)

This commit is contained in:
Tarek Ziadé 2009-07-16 15:35:45 +00:00
parent 7530e47948
commit a99dedfce2
7 changed files with 235 additions and 110 deletions

View File

@ -50,16 +50,15 @@ __revision__ = "$Id$"
import os import os
import sys import sys
import copy import copy
from subprocess import Popen, PIPE
import re import re
from warnings import warn
from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.ccompiler import gen_preprocess_options, gen_lib_options
from distutils.unixccompiler import UnixCCompiler from distutils.unixccompiler import UnixCCompiler
from distutils.file_util import write_file from distutils.file_util import write_file
from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
from distutils import log from distutils import log
from distutils.version import LooseVersion from distutils.util import get_compiler_versions
from distutils.spawn import find_executable
def get_msvcr(): def get_msvcr():
"""Include the appropriate MSVC runtime library if Python was built """Include the appropriate MSVC runtime library if Python was built
@ -110,7 +109,7 @@ class CygwinCCompiler(UnixCCompiler):
% details) % details)
self.gcc_version, self.ld_version, self.dllwrap_version = \ self.gcc_version, self.ld_version, self.dllwrap_version = \
get_versions() get_compiler_versions()
self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
(self.gcc_version, (self.gcc_version,
self.ld_version, self.ld_version,
@ -359,31 +358,26 @@ def check_config_h():
return (CONFIG_H_UNCERTAIN, return (CONFIG_H_UNCERTAIN,
"couldn't read '%s': %s" % (fn, exc.strerror)) "couldn't read '%s': %s" % (fn, exc.strerror))
RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') class _Deprecated_SRE_Pattern(object):
def __init__(self, pattern):
self.pattern = pattern
def _find_exe_version(cmd): def __getattr__(self, name):
"""Find the version of an executable by running `cmd` in the shell. if name in ('findall', 'finditer', 'match', 'scanner', 'search',
'split', 'sub', 'subn'):
warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated "
"and will be removed in the next version", DeprecationWarning)
return getattr(self.pattern, name)
If the command is not found, or the output does not match RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)'))
`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(): def get_versions():
""" Try to find out the versions of gcc, ld and dllwrap. """ 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.
""" """
commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] warn("'distutils.cygwinccompiler.get_versions' is deprecated "
return tuple([_find_exe_version(cmd) for cmd in commands]) "use 'distutils.util.get_compiler_versions' instead",
DeprecationWarning)
return get_compiler_versions()

View File

@ -21,12 +21,15 @@ handles the EMX port of the GNU C compiler to OS/2.
__revision__ = "$Id$" __revision__ = "$Id$"
import os,sys,copy import os, sys, copy
from warnings import warn
from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.ccompiler import gen_preprocess_options, gen_lib_options
from distutils.unixccompiler import UnixCCompiler from distutils.unixccompiler import UnixCCompiler
from distutils.file_util import write_file from distutils.file_util import write_file
from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils.errors import DistutilsExecError, CompileError, UnknownFileError
from distutils import log from distutils import log
from distutils.util import get_compiler_versions
class EMXCCompiler (UnixCCompiler): class EMXCCompiler (UnixCCompiler):
@ -55,8 +58,8 @@ class EMXCCompiler (UnixCCompiler):
("Reason: %s." % details) + ("Reason: %s." % details) +
"Compiling may fail because of undefined preprocessor macros.") "Compiling may fail because of undefined preprocessor macros.")
(self.gcc_version, self.ld_version) = \ gcc_version, ld_version, dllwrap_version = get_compiler_versions()
get_versions() self.gcc_version, self.ld_version = gcc_version, ld_version
self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" %
(self.gcc_version, (self.gcc_version,
self.ld_version) ) self.ld_version) )
@ -293,23 +296,11 @@ def get_versions():
""" Try to find out the versions of gcc and ld. """ Try to find out the versions of gcc and ld.
If not possible it returns None for it. If not possible it returns None for it.
""" """
from distutils.version import StrictVersion warn("'distutils.emxccompiler.get_versions' is deprecated "
from distutils.spawn import find_executable "use 'distutils.util.get_compiler_versions' instead",
import re DeprecationWarning)
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)
if result:
gcc_version = StrictVersion(result.group(1))
else:
gcc_version = None
else:
gcc_version = None
# EMX ld has no way of reporting version number, and we use GCC # EMX ld has no way of reporting version number, and we use GCC
# anyway - so we can link OMF DLLs # anyway - so we can link OMF DLLs
ld_version = None gcc_version, ld_version, dllwrap_version = get_compiler_versions()
return (gcc_version, ld_version) return gcc_version, None

View File

@ -2,28 +2,19 @@
import unittest import unittest
import sys import sys
import os import os
from StringIO import StringIO import warnings
import subprocess
from test.test_support import check_warnings
from test.test_support import captured_stdout
from distutils import cygwinccompiler from distutils import cygwinccompiler
from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h,
CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_OK, CONFIG_H_NOTOK,
CONFIG_H_UNCERTAIN, get_versions, CONFIG_H_UNCERTAIN, get_versions,
get_msvcr) get_msvcr, RE_VERSION)
from distutils.util import get_compiler_versions
from distutils.tests import support 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, class CygwinCCompilerTestCase(support.TempdirManager,
unittest.TestCase): unittest.TestCase):
@ -34,29 +25,16 @@ class CygwinCCompilerTestCase(support.TempdirManager,
from distutils import sysconfig from distutils import sysconfig
self.old_get_config_h_filename = sysconfig.get_config_h_filename self.old_get_config_h_filename = sysconfig.get_config_h_filename
sysconfig.get_config_h_filename = self._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): def tearDown(self):
sys.version = self.version sys.version = self.version
from distutils import sysconfig from distutils import sysconfig
sysconfig.get_config_h_filename = self.old_get_config_h_filename 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() super(CygwinCCompilerTestCase, self).tearDown()
def _get_config_h_filename(self): def _get_config_h_filename(self):
return self.python_h return self.python_h
def _find_executable(self, name):
if name in self._exes:
return name
return None
def test_check_config_h(self): def test_check_config_h(self):
# check_config_h looks for "GCC" in sys.version first # check_config_h looks for "GCC" in sys.version first
@ -80,40 +58,6 @@ class CygwinCCompilerTestCase(support.TempdirManager,
self.write_file(self.python_h, 'xxx __GNUC__ xxx') self.write_file(self.python_h, 'xxx __GNUC__ xxx')
self.assertEquals(check_config_h()[0], CONFIG_H_OK) 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_get_msvcr(self): def test_get_msvcr(self):
# none # none
@ -146,6 +90,21 @@ class CygwinCCompilerTestCase(support.TempdirManager,
'[MSC v.1999 32 bits (Intel)]') '[MSC v.1999 32 bits (Intel)]')
self.assertRaises(ValueError, get_msvcr) self.assertRaises(ValueError, get_msvcr)
def test_get_version_deprecated(self):
with check_warnings() as w:
warnings.simplefilter("always")
# make sure get_compiler_versions and get_versions
# returns the same thing
self.assertEquals(get_compiler_versions(), get_versions())
# make sure using get_version() generated a warning
self.assertEquals(len(w.warnings), 1)
# make sure any usage of RE_VERSION will also
# generate a warning, but till works
version = RE_VERSION.search('1.2').group(1)
self.assertEquals(version, '1.2')
self.assertEquals(len(w.warnings), 2)
def test_suite(): def test_suite():
return unittest.makeSuite(CygwinCCompilerTestCase) return unittest.makeSuite(CygwinCCompilerTestCase)

View File

@ -0,0 +1,33 @@
"""Tests for distutils.emxccompiler."""
import unittest
import sys
import os
import warnings
from test.test_support import check_warnings
from test.test_support import captured_stdout
from distutils.emxccompiler import get_versions
from distutils.util import get_compiler_versions
from distutils.tests import support
class EmxCCompilerTestCase(support.TempdirManager,
unittest.TestCase):
def test_get_version_deprecated(self):
with check_warnings() as w:
warnings.simplefilter("always")
# make sure get_compiler_versions and get_versions
# returns the same gcc
gcc, ld, dllwrap = get_compiler_versions()
emx_gcc, emx_ld = get_versions()
self.assertEquals(gcc, emx_gcc)
# make sure using get_version() generated a warning
self.assertEquals(len(w.warnings), 1)
def test_suite():
return unittest.makeSuite(EmxCCompilerTestCase)
if __name__ == '__main__':
test_support.run_unittest(test_suite())

View File

@ -6,15 +6,33 @@ import os
import sys import sys
import unittest import unittest
from copy import copy from copy import copy
from StringIO import StringIO
import subprocess
from distutils.errors import DistutilsPlatformError from distutils.errors import DistutilsPlatformError
from distutils.util import (get_platform, convert_path, change_root, from distutils.util import (get_platform, convert_path, change_root,
check_environ, split_quoted, strtobool, check_environ, split_quoted, strtobool,
rfc822_escape) rfc822_escape, get_compiler_versions,
from distutils import util # used to patch _environ_checked _find_exe_version, _MAC_OS_X_LD_VERSION)
from distutils import util
from distutils.sysconfig import get_config_vars from distutils.sysconfig import get_config_vars
from distutils import sysconfig from distutils import sysconfig
from distutils.tests import support from distutils.tests import support
from distutils.version import LooseVersion
class FakePopen(object):
test_class = None
def __init__(self, cmd, shell, stdout, stderr):
self.cmd = cmd.split()[0]
exes = self.test_class._exes
if self.cmd not in exes:
# we don't want to call the system, returning an empty
# output so it doesn't match
self.stdout = StringIO()
self.stderr = StringIO()
else:
self.stdout = StringIO(exes[self.cmd])
self.stderr = StringIO()
class UtilTestCase(support.EnvironGuard, unittest.TestCase): class UtilTestCase(support.EnvironGuard, unittest.TestCase):
@ -37,9 +55,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
else: else:
self.uname = None self.uname = None
self._uname = None self._uname = None
os.uname = self._get_uname os.uname = self._get_uname
# patching POpen
self.old_find_executable = util.find_executable
util.find_executable = self._find_executable
self._exes = {}
self.old_popen = subprocess.Popen
FakePopen.test_class = self
subprocess.Popen = FakePopen
def tearDown(self): def tearDown(self):
# getting back the environment # getting back the environment
os.name = self.name os.name = self.name
@ -54,6 +79,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
else: else:
del os.uname del os.uname
sysconfig._config_vars = copy(self._config_vars) sysconfig._config_vars = copy(self._config_vars)
util.find_executable = self.old_find_executable
subprocess.Popen = self.old_popen
super(UtilTestCase, self).tearDown() super(UtilTestCase, self).tearDown()
def _set_uname(self, uname): def _set_uname(self, uname):
@ -237,6 +264,70 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
'header%(8s)s') % {'8s': '\n'+8*' '} 'header%(8s)s') % {'8s': '\n'+8*' '}
self.assertEquals(res, wanted) self.assertEquals(res, wanted)
def test_find_exe_version(self):
# the ld version scheme under MAC OS is:
# ^@(#)PROGRAM:ld PROJECT:ld64-VERSION
#
# where VERSION is a 2-digit number for major
# revisions. For instance under Leopard, it's
# currently 77
#
# Dots are used when branching is done.
#
# The SnowLeopard ld64 is currently 95.2.12
for output, version in (('@(#)PROGRAM:ld PROJECT:ld64-77', '77'),
('@(#)PROGRAM:ld PROJECT:ld64-95.2.12',
'95.2.12')):
result = _MAC_OS_X_LD_VERSION.search(output)
self.assertEquals(result.group(1), version)
def _find_executable(self, name):
if name in self._exes:
return name
return None
def test_get_compiler_versions(self):
# get_versions calls distutils.spawn.find_executable on
# 'gcc', 'ld' and 'dllwrap'
self.assertEquals(get_compiler_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_compiler_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_compiler_versions()
self.assertEquals(res[0], None)
# same thing for ld
if sys.platform != 'darwin':
self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
res = get_compiler_versions()
self.assertEquals(str(res[1]), '2.17.50')
self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77'
res = get_compiler_versions()
self.assertEquals(res[1], None)
else:
self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
res = get_compiler_versions()
self.assertEquals(res[1], None)
self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77'
res = get_compiler_versions()
self.assertEquals(str(res[1]), '77')
# and dllwrap
self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
res = get_compiler_versions()
self.assertEquals(str(res[2]), '2.17.50')
self._exes['dllwrap'] = 'Cheese Wrap'
res = get_compiler_versions()
self.assertEquals(res[2], None)
def test_suite(): def test_suite():
return unittest.makeSuite(UtilTestCase) return unittest.makeSuite(UtilTestCase)

View File

@ -7,10 +7,12 @@ one of the other *util.py modules.
__revision__ = "$Id$" __revision__ = "$Id$"
import sys, os, string, re import sys, os, string, re
from distutils.errors import DistutilsPlatformError from distutils.errors import DistutilsPlatformError
from distutils.dep_util import newer from distutils.dep_util import newer
from distutils.spawn import spawn from distutils.spawn import spawn, find_executable
from distutils import log from distutils import log
from distutils.version import LooseVersion
def get_platform(): def get_platform():
"""Return a string that identifies the current platform. """Return a string that identifies the current platform.
@ -539,3 +541,53 @@ def rfc822_escape(header):
lines = [x.strip() for x in header.split('\n')] lines = [x.strip() for x in header.split('\n')]
sep = '\n' + 8*' ' sep = '\n' + 8*' '
return sep.join(lines) return sep.join(lines)
_RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)')
_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)')
def _find_ld_version():
"""Finds the ld version. The version scheme differs under Mac OSX."""
if sys.platform == 'darwin':
return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION)
else:
return _find_exe_version('ld -v')
def _find_exe_version(cmd, pattern=_RE_VERSION):
"""Find the version of an executable by running `cmd` in the shell.
`pattern` is a compiled regular expression. If not provided, default
to _RE_VERSION. If the command is not found, or the output does not
match the mattern, returns None.
"""
from subprocess import Popen, PIPE
executable = cmd.split()[0]
if find_executable(executable) is None:
return None
pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
try:
stdout, stderr = pipe.stdout.read(), pipe.stderr.read()
finally:
pipe.stdout.close()
pipe.stderr.close()
# some commands like ld under MacOS X, will give the
# output in the stderr, rather than stdout.
if stdout != '':
out_string = stdout
else:
out_string = stderr
result = pattern.search(out_string)
if result is None:
return None
return LooseVersion(result.group(1))
def get_compiler_versions():
"""Returns a tuple providing the versions of gcc, ld and dllwrap
For each command, if a command is not found, None is returned.
Otherwise a LooseVersion instance is returned.
"""
gcc = _find_exe_version('gcc -dumpversion')
ld = _find_ld_version()
dllwrap = _find_exe_version('dllwrap --version')
return gcc, ld, dllwrap

View File

@ -352,6 +352,11 @@ Core and Builtins
Library Library
------- -------
- Issue #6466: now distutils.cygwinccompiler and distutils.emxccompiler
uses the same refactored function to get gcc/ld/dllwrap versions numbers.
It's `distutils.util.get_compiler_versions`. Added deprecation warnings
for the obsolete get_versions() functions.
- Issue #6433: fixed issues with multiprocessing.pool.map hanging on empty list - Issue #6433: fixed issues with multiprocessing.pool.map hanging on empty list
- Issue #6314: logging: Extra checks on the "level" argument in more places. - Issue #6314: logging: Extra checks on the "level" argument in more places.