• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Tests for distutils.sysconfig."""
2import contextlib
3import os
4import shutil
5import subprocess
6import sys
7import textwrap
8import unittest
9
10from distutils import sysconfig
11from distutils.ccompiler import get_default_compiler
12from distutils.tests import support
13from test.support import run_unittest, swap_item
14from test.support.os_helper import TESTFN
15from test.support.warnings_helper import check_warnings
16
17
18class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
19    def setUp(self):
20        super(SysconfigTestCase, self).setUp()
21        self.makefile = None
22
23    def tearDown(self):
24        if self.makefile is not None:
25            os.unlink(self.makefile)
26        self.cleanup_testfn()
27        super(SysconfigTestCase, self).tearDown()
28
29    def cleanup_testfn(self):
30        if os.path.isfile(TESTFN):
31            os.remove(TESTFN)
32        elif os.path.isdir(TESTFN):
33            shutil.rmtree(TESTFN)
34
35    def test_get_config_h_filename(self):
36        config_h = sysconfig.get_config_h_filename()
37        self.assertTrue(os.path.isfile(config_h), config_h)
38
39    def test_get_python_lib(self):
40        # XXX doesn't work on Linux when Python was never installed before
41        #self.assertTrue(os.path.isdir(lib_dir), lib_dir)
42        # test for pythonxx.lib?
43        self.assertNotEqual(sysconfig.get_python_lib(),
44                            sysconfig.get_python_lib(prefix=TESTFN))
45
46    def test_get_config_vars(self):
47        cvars = sysconfig.get_config_vars()
48        self.assertIsInstance(cvars, dict)
49        self.assertTrue(cvars)
50
51    def test_srcdir(self):
52        # See Issues #15322, #15364.
53        srcdir = sysconfig.get_config_var('srcdir')
54
55        self.assertTrue(os.path.isabs(srcdir), srcdir)
56        self.assertTrue(os.path.isdir(srcdir), srcdir)
57
58        if sysconfig.python_build:
59            # The python executable has not been installed so srcdir
60            # should be a full source checkout.
61            Python_h = os.path.join(srcdir, 'Include', 'Python.h')
62            self.assertTrue(os.path.exists(Python_h), Python_h)
63            self.assertTrue(sysconfig._is_python_source_dir(srcdir))
64        elif os.name == 'posix':
65            self.assertEqual(
66                os.path.dirname(sysconfig.get_makefile_filename()),
67                srcdir)
68
69    def test_srcdir_independent_of_cwd(self):
70        # srcdir should be independent of the current working directory
71        # See Issues #15322, #15364.
72        srcdir = sysconfig.get_config_var('srcdir')
73        cwd = os.getcwd()
74        try:
75            os.chdir('..')
76            srcdir2 = sysconfig.get_config_var('srcdir')
77        finally:
78            os.chdir(cwd)
79        self.assertEqual(srcdir, srcdir2)
80
81    def customize_compiler(self):
82        # make sure AR gets caught
83        class compiler:
84            compiler_type = 'unix'
85
86            def set_executables(self, **kw):
87                self.exes = kw
88
89        sysconfig_vars = {
90            'AR': 'sc_ar',
91            'CC': 'sc_cc',
92            'CXX': 'sc_cxx',
93            'ARFLAGS': '--sc-arflags',
94            'CFLAGS': '--sc-cflags',
95            'CCSHARED': '--sc-ccshared',
96            'LDSHARED': 'sc_ldshared',
97            'SHLIB_SUFFIX': 'sc_shutil_suffix',
98
99            # On macOS, disable _osx_support.customize_compiler()
100            'CUSTOMIZED_OSX_COMPILER': 'True',
101        }
102
103        comp = compiler()
104        with contextlib.ExitStack() as cm:
105            for key, value in sysconfig_vars.items():
106                cm.enter_context(swap_item(sysconfig._config_vars, key, value))
107            sysconfig.customize_compiler(comp)
108
109        return comp
110
111    @unittest.skipUnless(get_default_compiler() == 'unix',
112                         'not testing if default compiler is not unix')
113    def test_customize_compiler(self):
114        # Make sure that sysconfig._config_vars is initialized
115        sysconfig.get_config_vars()
116
117        os.environ['AR'] = 'env_ar'
118        os.environ['CC'] = 'env_cc'
119        os.environ['CPP'] = 'env_cpp'
120        os.environ['CXX'] = 'env_cxx --env-cxx-flags'
121        os.environ['LDSHARED'] = 'env_ldshared'
122        os.environ['LDFLAGS'] = '--env-ldflags'
123        os.environ['ARFLAGS'] = '--env-arflags'
124        os.environ['CFLAGS'] = '--env-cflags'
125        os.environ['CPPFLAGS'] = '--env-cppflags'
126
127        comp = self.customize_compiler()
128        self.assertEqual(comp.exes['archiver'],
129                         'env_ar --env-arflags')
130        self.assertEqual(comp.exes['preprocessor'],
131                         'env_cpp --env-cppflags')
132        self.assertEqual(comp.exes['compiler'],
133                         'env_cc --sc-cflags --env-cflags --env-cppflags')
134        self.assertEqual(comp.exes['compiler_so'],
135                         ('env_cc --sc-cflags '
136                          '--env-cflags ''--env-cppflags --sc-ccshared'))
137        self.assertEqual(comp.exes['compiler_cxx'],
138                         'env_cxx --env-cxx-flags')
139        self.assertEqual(comp.exes['linker_exe'],
140                         'env_cc')
141        self.assertEqual(comp.exes['linker_so'],
142                         ('env_ldshared --env-ldflags --env-cflags'
143                          ' --env-cppflags'))
144        self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix')
145
146        del os.environ['AR']
147        del os.environ['CC']
148        del os.environ['CPP']
149        del os.environ['CXX']
150        del os.environ['LDSHARED']
151        del os.environ['LDFLAGS']
152        del os.environ['ARFLAGS']
153        del os.environ['CFLAGS']
154        del os.environ['CPPFLAGS']
155
156        comp = self.customize_compiler()
157        self.assertEqual(comp.exes['archiver'],
158                         'sc_ar --sc-arflags')
159        self.assertEqual(comp.exes['preprocessor'],
160                         'sc_cc -E')
161        self.assertEqual(comp.exes['compiler'],
162                         'sc_cc --sc-cflags')
163        self.assertEqual(comp.exes['compiler_so'],
164                         'sc_cc --sc-cflags --sc-ccshared')
165        self.assertEqual(comp.exes['compiler_cxx'],
166                         'sc_cxx')
167        self.assertEqual(comp.exes['linker_exe'],
168                         'sc_cc')
169        self.assertEqual(comp.exes['linker_so'],
170                         'sc_ldshared')
171        self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix')
172
173    def test_parse_makefile_base(self):
174        self.makefile = TESTFN
175        fd = open(self.makefile, 'w')
176        try:
177            fd.write(r"CONFIG_ARGS=  '--arg1=optarg1' 'ENV=LIB'" '\n')
178            fd.write('VAR=$OTHER\nOTHER=foo')
179        finally:
180            fd.close()
181        d = sysconfig.parse_makefile(self.makefile)
182        self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'",
183                             'OTHER': 'foo'})
184
185    def test_parse_makefile_literal_dollar(self):
186        self.makefile = TESTFN
187        fd = open(self.makefile, 'w')
188        try:
189            fd.write(r"CONFIG_ARGS=  '--arg1=optarg1' 'ENV=\$$LIB'" '\n')
190            fd.write('VAR=$OTHER\nOTHER=foo')
191        finally:
192            fd.close()
193        d = sysconfig.parse_makefile(self.makefile)
194        self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'",
195                             'OTHER': 'foo'})
196
197
198    def test_sysconfig_module(self):
199        import sysconfig as global_sysconfig
200        self.assertEqual(global_sysconfig.get_config_var('CFLAGS'),
201                         sysconfig.get_config_var('CFLAGS'))
202        self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'),
203                         sysconfig.get_config_var('LDFLAGS'))
204
205    @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),
206                     'compiler flags customized')
207    def test_sysconfig_compiler_vars(self):
208        # On OS X, binary installers support extension module building on
209        # various levels of the operating system with differing Xcode
210        # configurations.  This requires customization of some of the
211        # compiler configuration directives to suit the environment on
212        # the installed machine.  Some of these customizations may require
213        # running external programs and, so, are deferred until needed by
214        # the first extension module build.  With Python 3.3, only
215        # the Distutils version of sysconfig is used for extension module
216        # builds, which happens earlier in the Distutils tests.  This may
217        # cause the following tests to fail since no tests have caused
218        # the global version of sysconfig to call the customization yet.
219        # The solution for now is to simply skip this test in this case.
220        # The longer-term solution is to only have one version of sysconfig.
221
222        import sysconfig as global_sysconfig
223        if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
224            self.skipTest('compiler flags customized')
225        self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),
226                         sysconfig.get_config_var('LDSHARED'))
227        self.assertEqual(global_sysconfig.get_config_var('CC'),
228                         sysconfig.get_config_var('CC'))
229
230    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
231                     'EXT_SUFFIX required for this test')
232    def test_SO_deprecation(self):
233        self.assertWarns(DeprecationWarning,
234                         sysconfig.get_config_var, 'SO')
235
236    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
237                     'EXT_SUFFIX required for this test')
238    def test_SO_value(self):
239        with check_warnings(('', DeprecationWarning)):
240            self.assertEqual(sysconfig.get_config_var('SO'),
241                             sysconfig.get_config_var('EXT_SUFFIX'))
242
243    @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None,
244                     'EXT_SUFFIX required for this test')
245    def test_SO_in_vars(self):
246        vars = sysconfig.get_config_vars()
247        self.assertIsNotNone(vars['SO'])
248        self.assertEqual(vars['SO'], vars['EXT_SUFFIX'])
249
250    def test_customize_compiler_before_get_config_vars(self):
251        # Issue #21923: test that a Distribution compiler
252        # instance can be called without an explicit call to
253        # get_config_vars().
254        with open(TESTFN, 'w') as f:
255            f.writelines(textwrap.dedent('''\
256                from distutils.core import Distribution
257                config = Distribution().get_command_obj('config')
258                # try_compile may pass or it may fail if no compiler
259                # is found but it should not raise an exception.
260                rc = config.try_compile('int x;')
261                '''))
262        p = subprocess.Popen([str(sys.executable), TESTFN],
263                stdout=subprocess.PIPE,
264                stderr=subprocess.STDOUT,
265                universal_newlines=True)
266        outs, errs = p.communicate()
267        self.assertEqual(0, p.returncode, "Subprocess failed: " + outs)
268
269
270def test_suite():
271    suite = unittest.TestSuite()
272    suite.addTest(unittest.makeSuite(SysconfigTestCase))
273    return suite
274
275
276if __name__ == '__main__':
277    run_unittest(test_suite())
278