1"""
2Test suite for _osx_support: shared OS X support functions.
3"""
4
5import os
6import platform
7import shutil
8import stat
9import sys
10import unittest
11
12import test.test_support
13
14import _osx_support
15
16@unittest.skipUnless(sys.platform.startswith("darwin"), "requires OS X")
17class Test_OSXSupport(unittest.TestCase):
18
19    def setUp(self):
20        self.maxDiff = None
21        self.prog_name = 'bogus_program_xxxx'
22        self.temp_path_dir = os.path.abspath(os.getcwd())
23        self.env = test.test_support.EnvironmentVarGuard()
24        self.addCleanup(self.env.__exit__)
25        for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS',
26                            'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC',
27                            'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS',
28                            'PY_CORE_CFLAGS'):
29            if cv in self.env:
30                self.env.unset(cv)
31
32    def add_expected_saved_initial_values(self, config_vars, expected_vars):
33        # Ensure that the initial values for all modified config vars
34        # are also saved with modified keys.
35        expected_vars.update(('_OSX_SUPPORT_INITIAL_'+ k,
36                config_vars[k]) for k in config_vars
37                    if config_vars[k] != expected_vars[k])
38
39    def test__find_executable(self):
40        if self.env['PATH']:
41            self.env['PATH'] = self.env['PATH'] + ':'
42        self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
43        test.test_support.unlink(self.prog_name)
44        self.assertIsNone(_osx_support._find_executable(self.prog_name))
45        self.addCleanup(test.test_support.unlink, self.prog_name)
46        with open(self.prog_name, 'w') as f:
47            f.write("#!/bin/sh\n/bin/echo OK\n")
48        os.chmod(self.prog_name, stat.S_IRWXU)
49        self.assertEqual(self.prog_name,
50                            _osx_support._find_executable(self.prog_name))
51
52    def test__read_output(self):
53        if self.env['PATH']:
54            self.env['PATH'] = self.env['PATH'] + ':'
55        self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
56        test.test_support.unlink(self.prog_name)
57        self.addCleanup(test.test_support.unlink, self.prog_name)
58        with open(self.prog_name, 'w') as f:
59            f.write("#!/bin/sh\n/bin/echo ExpectedOutput\n")
60        os.chmod(self.prog_name, stat.S_IRWXU)
61        self.assertEqual('ExpectedOutput',
62                            _osx_support._read_output(self.prog_name))
63
64    def test__find_build_tool(self):
65        out = _osx_support._find_build_tool('cc')
66        self.assertTrue(os.path.isfile(out),
67                            'cc not found - check xcode-select')
68
69    def test__get_system_version(self):
70        self.assertTrue(platform.mac_ver()[0].startswith(
71                                    _osx_support._get_system_version()))
72
73    def test__remove_original_values(self):
74        config_vars = {
75        'CC': 'gcc-test -pthreads',
76        }
77        expected_vars = {
78        'CC': 'clang -pthreads',
79        }
80        cv = 'CC'
81        newvalue = 'clang -pthreads'
82        _osx_support._save_modified_value(config_vars, cv, newvalue)
83        self.assertNotEqual(expected_vars, config_vars)
84        _osx_support._remove_original_values(config_vars)
85        self.assertEqual(expected_vars, config_vars)
86
87    def test__save_modified_value(self):
88        config_vars = {
89        'CC': 'gcc-test -pthreads',
90        }
91        expected_vars = {
92        'CC': 'clang -pthreads',
93        }
94        self.add_expected_saved_initial_values(config_vars, expected_vars)
95        cv = 'CC'
96        newvalue = 'clang -pthreads'
97        _osx_support._save_modified_value(config_vars, cv, newvalue)
98        self.assertEqual(expected_vars, config_vars)
99
100    def test__save_modified_value_unchanged(self):
101        config_vars = {
102        'CC': 'gcc-test -pthreads',
103        }
104        expected_vars = config_vars.copy()
105        cv = 'CC'
106        newvalue = 'gcc-test -pthreads'
107        _osx_support._save_modified_value(config_vars, cv, newvalue)
108        self.assertEqual(expected_vars, config_vars)
109
110    def test__supports_universal_builds(self):
111        import platform
112        self.assertEqual(platform.mac_ver()[0].split('.') >= ['10', '4'],
113                            _osx_support._supports_universal_builds())
114
115    def test__find_appropriate_compiler(self):
116        compilers = (
117                        ('gcc-test', 'i686-apple-darwin11-llvm-gcc-4.2'),
118                        ('clang', 'clang version 3.1'),
119                    )
120        config_vars = {
121        'CC': 'gcc-test -pthreads',
122        'CXX': 'cc++-test',
123        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
124        'LDFLAGS': '-arch ppc -arch i386   -g',
125        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
126        'BLDSHARED': 'gcc-test -bundle -arch ppc -arch i386 -g',
127        'LDSHARED': 'gcc-test -bundle -arch ppc -arch i386 '
128                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
129        }
130        expected_vars = {
131        'CC': 'clang -pthreads',
132        'CXX': 'clang++',
133        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
134        'LDFLAGS': '-arch ppc -arch i386   -g',
135        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
136        'BLDSHARED': 'clang -bundle -arch ppc -arch i386 -g',
137        'LDSHARED': 'clang -bundle -arch ppc -arch i386 '
138                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
139        }
140        self.add_expected_saved_initial_values(config_vars, expected_vars)
141
142        suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
143        self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
144        for c_name, c_output in compilers:
145            test.test_support.unlink(c_name)
146            self.addCleanup(test.test_support.unlink, c_name)
147            with open(c_name, 'w') as f:
148                f.write("#!/bin/sh\n/bin/echo " + c_output)
149            os.chmod(c_name, stat.S_IRWXU)
150        self.assertEqual(expected_vars,
151                            _osx_support._find_appropriate_compiler(
152                                    config_vars))
153
154    def test__remove_universal_flags(self):
155        config_vars = {
156        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
157        'LDFLAGS': '-arch ppc -arch i386   -g',
158        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
159        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
160        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
161                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
162        }
163        expected_vars = {
164        'CFLAGS': '-fno-strict-aliasing  -g -O3    ',
165        'LDFLAGS': '    -g',
166        'CPPFLAGS': '-I.  ',
167        'BLDSHARED': 'gcc-4.0 -bundle    -g',
168        'LDSHARED': 'gcc-4.0 -bundle      -g',
169        }
170        self.add_expected_saved_initial_values(config_vars, expected_vars)
171
172        self.assertEqual(expected_vars,
173                            _osx_support._remove_universal_flags(
174                                    config_vars))
175
176    def test__remove_unsupported_archs(self):
177        config_vars = {
178        'CC': 'clang',
179        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
180        'LDFLAGS': '-arch ppc -arch i386   -g',
181        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
182        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
183        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
184                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
185        }
186        expected_vars = {
187        'CC': 'clang',
188        'CFLAGS': '-fno-strict-aliasing  -g -O3  -arch i386  ',
189        'LDFLAGS': ' -arch i386   -g',
190        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
191        'BLDSHARED': 'gcc-4.0 -bundle   -arch i386 -g',
192        'LDSHARED': 'gcc-4.0 -bundle   -arch i386 '
193                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
194        }
195        self.add_expected_saved_initial_values(config_vars, expected_vars)
196
197        suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
198        self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
199        c_name = 'clang'
200        test.test_support.unlink(c_name)
201        self.addCleanup(test.test_support.unlink, c_name)
202        # exit status 255 means no PPC support in this compiler chain
203        with open(c_name, 'w') as f:
204            f.write("#!/bin/sh\nexit 255")
205        os.chmod(c_name, stat.S_IRWXU)
206        self.assertEqual(expected_vars,
207                            _osx_support._remove_unsupported_archs(
208                                    config_vars))
209
210    def test__override_all_archs(self):
211        self.env['ARCHFLAGS'] = '-arch x86_64'
212        config_vars = {
213        'CC': 'clang',
214        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
215        'LDFLAGS': '-arch ppc -arch i386   -g',
216        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
217        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
218        'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 '
219                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
220        }
221        expected_vars = {
222        'CC': 'clang',
223        'CFLAGS': '-fno-strict-aliasing  -g -O3     -arch x86_64',
224        'LDFLAGS': '    -g -arch x86_64',
225        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
226        'BLDSHARED': 'gcc-4.0 -bundle    -g -arch x86_64',
227        'LDSHARED': 'gcc-4.0 -bundle   -isysroot '
228                        '/Developer/SDKs/MacOSX10.4u.sdk -g -arch x86_64',
229        }
230        self.add_expected_saved_initial_values(config_vars, expected_vars)
231
232        self.assertEqual(expected_vars,
233                            _osx_support._override_all_archs(
234                                    config_vars))
235
236    def test__check_for_unavailable_sdk(self):
237        config_vars = {
238        'CC': 'clang',
239        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
240                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk',
241        'LDFLAGS': '-arch ppc -arch i386   -g',
242        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.1.sdk',
243        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
244        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
245                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk -g',
246        }
247        expected_vars = {
248        'CC': 'clang',
249        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
250                        ' ',
251        'LDFLAGS': '-arch ppc -arch i386   -g',
252        'CPPFLAGS': '-I.  ',
253        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
254        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
255                        ' -g',
256        }
257        self.add_expected_saved_initial_values(config_vars, expected_vars)
258
259        self.assertEqual(expected_vars,
260                            _osx_support._check_for_unavailable_sdk(
261                                    config_vars))
262
263    def test_get_platform_osx(self):
264        # Note, get_platform_osx is currently tested more extensively
265        # indirectly by test_sysconfig and test_distutils
266        config_vars = {
267        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
268                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk',
269        'MACOSX_DEPLOYMENT_TARGET': '10.6',
270        }
271        result = _osx_support.get_platform_osx(config_vars, ' ', ' ', ' ')
272        self.assertEqual(('macosx', '10.6', 'fat'), result)
273
274def test_main():
275    if sys.platform == 'darwin':
276        test.test_support.run_unittest(Test_OSXSupport)
277
278if __name__ == "__main__":
279    test_main()
280