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