PageRenderTime 237ms CodeModel.GetById 12ms app.highlight 193ms RepoModel.GetById 10ms app.codeStats 1ms

/Lib/distutils/tests/test_build_ext.py

http://unladen-swallow.googlecode.com/
Python | 401 lines | 340 code | 31 blank | 30 comment | 12 complexity | d22cf60c913dfc168a3e1133a3d78683 MD5 | raw file
  1import sys
  2import os
  3import tempfile
  4import shutil
  5from StringIO import StringIO
  6
  7from distutils.core import Extension, Distribution
  8from distutils.command.build_ext import build_ext
  9from distutils import sysconfig
 10from distutils.tests import support
 11from distutils.errors import DistutilsSetupError
 12
 13import unittest
 14from test import test_support
 15
 16
 17# http://bugs.python.org/issue4373
 18# Don't load the xx module more than once.
 19ALREADY_TESTED = False
 20
 21def _get_source_filename():
 22    srcdir = sysconfig.get_config_var('srcdir')
 23    return os.path.join(srcdir, 'Modules', 'xxmodule.c')
 24
 25class BuildExtTestCase(support.TempdirManager,
 26                       support.LoggingSilencer,
 27                       unittest.TestCase):
 28    def setUp(self):
 29        # Create a simple test environment
 30        # Note that we're making changes to sys.path
 31        super(BuildExtTestCase, self).setUp()
 32        self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_")
 33        self.sys_path = sys.path[:]
 34        sys.path.append(self.tmp_dir)
 35        shutil.copy(_get_source_filename(), self.tmp_dir)
 36
 37    def test_build_ext(self):
 38        global ALREADY_TESTED
 39        xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
 40        xx_ext = Extension('xx', [xx_c])
 41        dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
 42        dist.package_dir = self.tmp_dir
 43        cmd = build_ext(dist)
 44        if os.name == "nt":
 45            # On Windows, we must build a debug version iff running
 46            # a debug build of Python
 47            cmd.debug = sys.executable.endswith("_d.exe")
 48        cmd.build_lib = self.tmp_dir
 49        cmd.build_temp = self.tmp_dir
 50
 51        old_stdout = sys.stdout
 52        if not test_support.verbose:
 53            # silence compiler output
 54            sys.stdout = StringIO()
 55        try:
 56            cmd.ensure_finalized()
 57            cmd.run()
 58        finally:
 59            sys.stdout = old_stdout
 60
 61        if ALREADY_TESTED:
 62            return
 63        else:
 64            ALREADY_TESTED = True
 65
 66        import xx
 67
 68        for attr in ('error', 'foo', 'new', 'roj'):
 69            self.assert_(hasattr(xx, attr))
 70
 71        self.assertEquals(xx.foo(2, 5), 7)
 72        self.assertEquals(xx.foo(13,15), 28)
 73        self.assertEquals(xx.new().demo(), None)
 74        doc = 'This is a template module just for instruction.'
 75        self.assertEquals(xx.__doc__, doc)
 76        self.assert_(isinstance(xx.Null(), xx.Null))
 77        self.assert_(isinstance(xx.Str(), xx.Str))
 78
 79    def tearDown(self):
 80        # Get everything back to normal
 81        test_support.unload('xx')
 82        sys.path = self.sys_path
 83        # XXX on Windows the test leaves a directory with xx module in TEMP
 84        shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin')
 85        super(BuildExtTestCase, self).tearDown()
 86
 87    def test_solaris_enable_shared(self):
 88        dist = Distribution({'name': 'xx'})
 89        cmd = build_ext(dist)
 90        old = sys.platform
 91
 92        sys.platform = 'sunos' # fooling finalize_options
 93        from distutils.sysconfig import  _config_vars
 94        old_var = _config_vars.get('Py_ENABLE_SHARED')
 95        _config_vars['Py_ENABLE_SHARED'] = 1
 96        try:
 97            cmd.ensure_finalized()
 98        finally:
 99            sys.platform = old
100            if old_var is None:
101                del _config_vars['Py_ENABLE_SHARED']
102            else:
103                _config_vars['Py_ENABLE_SHARED'] = old_var
104
105        # make sure we get some library dirs under solaris
106        self.assert_(len(cmd.library_dirs) > 0)
107
108    def test_finalize_options(self):
109        # Make sure Python's include directories (for Python.h, pyconfig.h,
110        # etc.) are in the include search path.
111        modules = [Extension('foo', ['xxx'])]
112        dist = Distribution({'name': 'xx', 'ext_modules': modules})
113        cmd = build_ext(dist)
114        cmd.finalize_options()
115
116        from distutils import sysconfig
117        py_include = sysconfig.get_python_inc()
118        self.assert_(py_include in cmd.include_dirs)
119
120        plat_py_include = sysconfig.get_python_inc(plat_specific=1)
121        self.assert_(plat_py_include in cmd.include_dirs)
122
123        # make sure cmd.libraries is turned into a list
124        # if it's a string
125        cmd = build_ext(dist)
126        cmd.libraries = 'my_lib'
127        cmd.finalize_options()
128        self.assertEquals(cmd.libraries, ['my_lib'])
129
130        # make sure cmd.library_dirs is turned into a list
131        # if it's a string
132        cmd = build_ext(dist)
133        cmd.library_dirs = 'my_lib_dir'
134        cmd.finalize_options()
135        self.assert_('my_lib_dir' in cmd.library_dirs)
136
137        # make sure rpath is turned into a list
138        # if it's a list of os.pathsep's paths
139        cmd = build_ext(dist)
140        cmd.rpath = os.pathsep.join(['one', 'two'])
141        cmd.finalize_options()
142        self.assertEquals(cmd.rpath, ['one', 'two'])
143
144        # XXX more tests to perform for win32
145
146        # make sure define is turned into 2-tuples
147        # strings if they are ','-separated strings
148        cmd = build_ext(dist)
149        cmd.define = 'one,two'
150        cmd.finalize_options()
151        self.assertEquals(cmd.define, [('one', '1'), ('two', '1')])
152
153        # make sure undef is turned into a list of
154        # strings if they are ','-separated strings
155        cmd = build_ext(dist)
156        cmd.undef = 'one,two'
157        cmd.finalize_options()
158        self.assertEquals(cmd.undef, ['one', 'two'])
159
160        # make sure swig_opts is turned into a list
161        cmd = build_ext(dist)
162        cmd.swig_opts = None
163        cmd.finalize_options()
164        self.assertEquals(cmd.swig_opts, [])
165
166        cmd = build_ext(dist)
167        cmd.swig_opts = '1 2'
168        cmd.finalize_options()
169        self.assertEquals(cmd.swig_opts, ['1', '2'])
170
171    def test_check_extensions_list(self):
172        dist = Distribution()
173        cmd = build_ext(dist)
174        cmd.finalize_options()
175
176        #'extensions' option must be a list of Extension instances
177        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
178
179        # each element of 'ext_modules' option must be an
180        # Extension instance or 2-tuple
181        exts = [('bar', 'foo', 'bar'), 'foo']
182        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
183
184        # first element of each tuple in 'ext_modules'
185        # must be the extension name (a string) and match
186        # a python dotted-separated name
187        exts = [('foo-bar', '')]
188        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
189
190        # second element of each tuple in 'ext_modules'
191        # must be a ary (build info)
192        exts = [('foo.bar', '')]
193        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
194
195        # ok this one should pass
196        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
197                             'some': 'bar'})]
198        cmd.check_extensions_list(exts)
199        ext = exts[0]
200        self.assert_(isinstance(ext, Extension))
201
202        # check_extensions_list adds in ext the values passed
203        # when they are in ('include_dirs', 'library_dirs', 'libraries'
204        # 'extra_objects', 'extra_compile_args', 'extra_link_args')
205        self.assertEquals(ext.libraries, 'foo')
206        self.assert_(not hasattr(ext, 'some'))
207
208        # 'macros' element of build info dict must be 1- or 2-tuple
209        exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
210                'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
211        self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
212
213        exts[0][1]['macros'] = [('1', '2'), ('3',)]
214        cmd.check_extensions_list(exts)
215        self.assertEquals(exts[0].undef_macros, ['3'])
216        self.assertEquals(exts[0].define_macros, [('1', '2')])
217
218    def test_get_source_files(self):
219        modules = [Extension('foo', ['xxx'])]
220        dist = Distribution({'name': 'xx', 'ext_modules': modules})
221        cmd = build_ext(dist)
222        cmd.ensure_finalized()
223        self.assertEquals(cmd.get_source_files(), ['xxx'])
224
225    def test_compiler_option(self):
226        # cmd.compiler is an option and
227        # should not be overriden by a compiler instance
228        # when the command is run
229        dist = Distribution()
230        cmd = build_ext(dist)
231        cmd.compiler = 'unix'
232        cmd.ensure_finalized()
233        cmd.run()
234        self.assertEquals(cmd.compiler, 'unix')
235
236    def test_get_outputs(self):
237        tmp_dir = self.mkdtemp()
238        c_file = os.path.join(tmp_dir, 'foo.c')
239        self.write_file(c_file, 'void initfoo(void) {};\n')
240        ext = Extension('foo', [c_file])
241        dist = Distribution({'name': 'xx',
242                             'ext_modules': [ext]})
243        cmd = build_ext(dist)
244        cmd.ensure_finalized()
245        self.assertEquals(len(cmd.get_outputs()), 1)
246
247        if os.name == "nt":
248            cmd.debug = sys.executable.endswith("_d.exe")
249
250        cmd.build_lib = os.path.join(self.tmp_dir, 'build')
251        cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
252
253        # issue #5977 : distutils build_ext.get_outputs
254        # returns wrong result with --inplace
255        other_tmp_dir = os.path.realpath(self.mkdtemp())
256        old_wd = os.getcwd()
257        os.chdir(other_tmp_dir)
258        try:
259            cmd.inplace = 1
260            cmd.run()
261            so_file = cmd.get_outputs()[0]
262        finally:
263            os.chdir(old_wd)
264        self.assert_(os.path.exists(so_file))
265        self.assertEquals(os.path.splitext(so_file)[-1],
266                          sysconfig.get_config_var('SO'))
267        so_dir = os.path.dirname(so_file)
268        self.assertEquals(so_dir, other_tmp_dir)
269        cmd.compiler = None
270        cmd.inplace = 0
271        cmd.run()
272        so_file = cmd.get_outputs()[0]
273        self.assert_(os.path.exists(so_file))
274        self.assertEquals(os.path.splitext(so_file)[-1],
275                          sysconfig.get_config_var('SO'))
276        so_dir = os.path.dirname(so_file)
277        self.assertEquals(so_dir, cmd.build_lib)
278
279        # inplace = 0, cmd.package = 'bar'
280        build_py = cmd.get_finalized_command('build_py')
281        build_py.package_dir = {'': 'bar'}
282        path = cmd.get_ext_fullpath('foo')
283        # checking that the last directory is the build_dir
284        path = os.path.split(path)[0]
285        self.assertEquals(path, cmd.build_lib)
286
287        # inplace = 1, cmd.package = 'bar'
288        cmd.inplace = 1
289        other_tmp_dir = os.path.realpath(self.mkdtemp())
290        old_wd = os.getcwd()
291        os.chdir(other_tmp_dir)
292        try:
293            path = cmd.get_ext_fullpath('foo')
294        finally:
295            os.chdir(old_wd)
296        # checking that the last directory is bar
297        path = os.path.split(path)[0]
298        lastdir = os.path.split(path)[-1]
299        self.assertEquals(lastdir, 'bar')
300
301    def test_ext_fullpath(self):
302        ext = sysconfig.get_config_vars()['SO']
303        dist = Distribution()
304        cmd = build_ext(dist)
305        cmd.inplace = 1
306        cmd.distribution.package_dir = {'': 'src'}
307        cmd.distribution.packages = ['lxml', 'lxml.html']
308        curdir = os.getcwd()
309        wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
310        path = cmd.get_ext_fullpath('lxml.etree')
311        self.assertEquals(wanted, path)
312
313        # building lxml.etree not inplace
314        cmd.inplace = 0
315        cmd.build_lib = os.path.join(curdir, 'tmpdir')
316        wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
317        path = cmd.get_ext_fullpath('lxml.etree')
318        self.assertEquals(wanted, path)
319
320        # building twisted.runner.portmap not inplace
321        build_py = cmd.get_finalized_command('build_py')
322        build_py.package_dir = {}
323        cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
324        path = cmd.get_ext_fullpath('twisted.runner.portmap')
325        wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
326                              'portmap' + ext)
327        self.assertEquals(wanted, path)
328
329        # building twisted.runner.portmap inplace
330        cmd.inplace = 1
331        path = cmd.get_ext_fullpath('twisted.runner.portmap')
332        wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
333        self.assertEquals(wanted, path)
334
335    def test_build_ext_inplace(self):
336        etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
337        etree_ext = Extension('lxml.etree', [etree_c])
338        dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
339        cmd = build_ext(dist)
340        cmd.ensure_finalized()
341        cmd.inplace = 1
342        cmd.distribution.package_dir = {'': 'src'}
343        cmd.distribution.packages = ['lxml', 'lxml.html']
344        curdir = os.getcwd()
345        ext = sysconfig.get_config_var("SO")
346        wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
347        path = cmd.get_ext_fullpath('lxml.etree')
348        self.assertEquals(wanted, path)
349
350    def test_setuptools_compat(self):
351        from setuptools_build_ext import build_ext as setuptools_build_ext
352        from setuptools_extension import Extension
353
354        etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
355        etree_ext = Extension('lxml.etree', [etree_c])
356        dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
357        cmd = setuptools_build_ext(dist)
358        cmd.ensure_finalized()
359        cmd.inplace = 1
360        cmd.distribution.package_dir = {'': 'src'}
361        cmd.distribution.packages = ['lxml', 'lxml.html']
362        curdir = os.getcwd()
363        ext = sysconfig.get_config_var("SO")
364        wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
365        path = cmd.get_ext_fullpath('lxml.etree')
366        self.assertEquals(wanted, path)
367
368    def test_build_ext_path_with_os_sep(self):
369        dist = Distribution({'name': 'UpdateManager'})
370        cmd = build_ext(dist)
371        cmd.ensure_finalized()
372        ext = sysconfig.get_config_var("SO")
373        ext_name = os.path.join('UpdateManager', 'fdsend')
374        ext_path = cmd.get_ext_fullpath(ext_name)
375        wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext)
376        self.assertEquals(ext_path, wanted)
377
378    def test_build_ext_path_cross_platform(self):
379        if sys.platform != 'win32':
380            return
381        dist = Distribution({'name': 'UpdateManager'})
382        cmd = build_ext(dist)
383        cmd.ensure_finalized()
384        ext = sysconfig.get_config_var("SO")
385        # this needs to work even under win32
386        ext_name = 'UpdateManager/fdsend'
387        ext_path = cmd.get_ext_fullpath(ext_name)
388        wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext)
389        self.assertEquals(ext_path, wanted)
390
391def test_suite():
392    src = _get_source_filename()
393    if not os.path.exists(src):
394        if test_support.verbose:
395            print ('test_build_ext: Cannot find source code (test'
396                   ' must run in python build dir)')
397        return unittest.TestSuite()
398    else: return unittest.makeSuite(BuildExtTestCase)
399
400if __name__ == '__main__':
401    test_support.run_unittest(test_suite())