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