blob: d97a97eba07c5dac17b1f013d459e33459faf692 [file] [log] [blame]
Georg Brandlb533e262008-05-25 18:19:30 +00001import sys
2import os
Tarek Ziadéd7b5f662009-02-13 16:23:57 +00003import tempfile
Georg Brandlb533e262008-05-25 18:19:30 +00004import shutil
5from io import StringIO
Tarek Ziadédd07ebb2009-07-06 13:52:17 +00006import warnings
7from test.support import check_warnings
8from test.support import captured_stdout
Georg Brandlb533e262008-05-25 18:19:30 +00009
10from distutils.core import Extension, Distribution
11from distutils.command.build_ext import build_ext
12from distutils import sysconfig
Tarek Ziadéc1375d52009-02-14 14:35:51 +000013from distutils.tests.support import TempdirManager
Tarek Ziadéb2e36f12009-03-31 22:37:55 +000014from distutils.tests.support import LoggingSilencer
15from distutils.extension import Extension
Tarek Ziadé06fbee12009-05-10 10:34:01 +000016from distutils.errors import (UnknownFileError, DistutilsSetupError,
17 CompileError)
Georg Brandlb533e262008-05-25 18:19:30 +000018
19import unittest
20from test import support
21
Christian Heimes3e7e0692008-11-25 21:21:32 +000022# http://bugs.python.org/issue4373
23# Don't load the xx module more than once.
24ALREADY_TESTED = False
25
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +000026def _get_source_filename():
27 srcdir = sysconfig.get_config_var('srcdir')
28 return os.path.join(srcdir, 'Modules', 'xxmodule.c')
29
Tarek Ziadéb2e36f12009-03-31 22:37:55 +000030class BuildExtTestCase(TempdirManager,
31 LoggingSilencer,
32 unittest.TestCase):
Georg Brandlb533e262008-05-25 18:19:30 +000033 def setUp(self):
34 # Create a simple test environment
35 # Note that we're making changes to sys.path
Tarek Ziadé38e3d512009-02-27 12:58:56 +000036 super(BuildExtTestCase, self).setUp()
Tarek Ziadéc1375d52009-02-14 14:35:51 +000037 self.tmp_dir = self.mkdtemp()
Georg Brandlb533e262008-05-25 18:19:30 +000038 self.sys_path = sys.path[:]
39 sys.path.append(self.tmp_dir)
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +000040 shutil.copy(_get_source_filename(), self.tmp_dir)
Tarek Ziadé38e3d512009-02-27 12:58:56 +000041 if sys.version > "2.6":
42 import site
43 self.old_user_base = site.USER_BASE
44 site.USER_BASE = self.mkdtemp()
45 from distutils.command import build_ext
46 build_ext.USER_BASE = site.USER_BASE
Georg Brandlb533e262008-05-25 18:19:30 +000047
48 def test_build_ext(self):
Christian Heimes3e7e0692008-11-25 21:21:32 +000049 global ALREADY_TESTED
Georg Brandlb533e262008-05-25 18:19:30 +000050 xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
51 xx_ext = Extension('xx', [xx_c])
52 dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
53 dist.package_dir = self.tmp_dir
54 cmd = build_ext(dist)
Thomas Heller84b7f0c2008-05-26 11:51:44 +000055 if os.name == "nt":
56 # On Windows, we must build a debug version iff running
57 # a debug build of Python
58 cmd.debug = sys.executable.endswith("_d.exe")
Georg Brandlb533e262008-05-25 18:19:30 +000059 cmd.build_lib = self.tmp_dir
60 cmd.build_temp = self.tmp_dir
61
62 old_stdout = sys.stdout
63 if not support.verbose:
64 # silence compiler output
65 sys.stdout = StringIO()
66 try:
67 cmd.ensure_finalized()
68 cmd.run()
69 finally:
70 sys.stdout = old_stdout
71
Christian Heimes3e7e0692008-11-25 21:21:32 +000072 if ALREADY_TESTED:
73 return
74 else:
75 ALREADY_TESTED = True
76
Georg Brandlb533e262008-05-25 18:19:30 +000077 import xx
78
79 for attr in ('error', 'foo', 'new', 'roj'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000080 self.assertTrue(hasattr(xx, attr))
Georg Brandlb533e262008-05-25 18:19:30 +000081
82 self.assertEquals(xx.foo(2, 5), 7)
83 self.assertEquals(xx.foo(13,15), 28)
84 self.assertEquals(xx.new().demo(), None)
85 doc = 'This is a template module just for instruction.'
86 self.assertEquals(xx.__doc__, doc)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000087 self.assertTrue(isinstance(xx.Null(), xx.Null))
88 self.assertTrue(isinstance(xx.Str(), xx.Str))
Georg Brandlb533e262008-05-25 18:19:30 +000089
90 def tearDown(self):
91 # Get everything back to normal
92 support.unload('xx')
93 sys.path = self.sys_path
Tarek Ziadé38e3d512009-02-27 12:58:56 +000094 if sys.version > "2.6":
95 import site
96 site.USER_BASE = self.old_user_base
97 from distutils.command import build_ext
98 build_ext.USER_BASE = self.old_user_base
99 super(BuildExtTestCase, self).tearDown()
Georg Brandlb533e262008-05-25 18:19:30 +0000100
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000101 def test_solaris_enable_shared(self):
102 dist = Distribution({'name': 'xx'})
103 cmd = build_ext(dist)
104 old = sys.platform
105
106 sys.platform = 'sunos' # fooling finalize_options
107 from distutils.sysconfig import _config_vars
108 old_var = _config_vars.get('Py_ENABLE_SHARED')
109 _config_vars['Py_ENABLE_SHARED'] = 1
110 try:
111 cmd.ensure_finalized()
112 finally:
113 sys.platform = old
114 if old_var is None:
115 del _config_vars['Py_ENABLE_SHARED']
116 else:
117 _config_vars['Py_ENABLE_SHARED'] = old_var
118
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000119 # make sure we get some library dirs under solaris
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(len(cmd.library_dirs) > 0)
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000121
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000122 def test_user_site(self):
123 # site.USER_SITE was introduced in 2.6
124 if sys.version < '2.6':
125 return
126
127 import site
128 dist = Distribution({'name': 'xx'})
129 cmd = build_ext(dist)
130
Tarek Ziadébe720e02009-05-09 11:55:12 +0000131 # making sure the user option is there
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000132 options = [name for name, short, lable in
133 cmd.user_options]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue('user' in options)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000135
136 # setting a value
137 cmd.user = 1
138
139 # setting user based lib and include
140 lib = os.path.join(site.USER_BASE, 'lib')
141 incl = os.path.join(site.USER_BASE, 'include')
142 os.mkdir(lib)
143 os.mkdir(incl)
144
145 # let's run finalize
146 cmd.ensure_finalized()
147
148 # see if include_dirs and library_dirs
149 # were set
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000150 self.assertTrue(lib in cmd.library_dirs)
151 self.assertTrue(lib in cmd.rpath)
152 self.assertTrue(incl in cmd.include_dirs)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000153
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000154 def test_optional_extension(self):
155
156 # this extension will fail, but let's ignore this failure
157 # with the optional argument.
158 modules = [Extension('foo', ['xxx'], optional=False)]
159 dist = Distribution({'name': 'xx', 'ext_modules': modules})
160 cmd = build_ext(dist)
161 cmd.ensure_finalized()
Tarek Ziadé30911292009-03-31 22:50:54 +0000162 self.assertRaises((UnknownFileError, CompileError),
163 cmd.run) # should raise an error
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000164
165 modules = [Extension('foo', ['xxx'], optional=True)]
166 dist = Distribution({'name': 'xx', 'ext_modules': modules})
167 cmd = build_ext(dist)
168 cmd.ensure_finalized()
169 cmd.run() # should pass
170
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000171 def test_finalize_options(self):
172 # Make sure Python's include directories (for Python.h, pyconfig.h,
173 # etc.) are in the include search path.
174 modules = [Extension('foo', ['xxx'], optional=False)]
175 dist = Distribution({'name': 'xx', 'ext_modules': modules})
176 cmd = build_ext(dist)
177 cmd.finalize_options()
178
179 from distutils import sysconfig
180 py_include = sysconfig.get_python_inc()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000181 self.assertTrue(py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000182
183 plat_py_include = sysconfig.get_python_inc(plat_specific=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000184 self.assertTrue(plat_py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000185
186 # make sure cmd.libraries is turned into a list
187 # if it's a string
188 cmd = build_ext(dist)
189 cmd.libraries = 'my_lib'
190 cmd.finalize_options()
191 self.assertEquals(cmd.libraries, ['my_lib'])
192
193 # make sure cmd.library_dirs is turned into a list
194 # if it's a string
195 cmd = build_ext(dist)
196 cmd.library_dirs = 'my_lib_dir'
197 cmd.finalize_options()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000198 self.assertTrue('my_lib_dir' in cmd.library_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000199
200 # make sure rpath is turned into a list
201 # if it's a list of os.pathsep's paths
202 cmd = build_ext(dist)
203 cmd.rpath = os.pathsep.join(['one', 'two'])
204 cmd.finalize_options()
205 self.assertEquals(cmd.rpath, ['one', 'two'])
206
207 # XXX more tests to perform for win32
208
209 # make sure define is turned into 2-tuples
210 # strings if they are ','-separated strings
211 cmd = build_ext(dist)
212 cmd.define = 'one,two'
213 cmd.finalize_options()
214 self.assertEquals(cmd.define, [('one', '1'), ('two', '1')])
215
216 # make sure undef is turned into a list of
217 # strings if they are ','-separated strings
218 cmd = build_ext(dist)
219 cmd.undef = 'one,two'
220 cmd.finalize_options()
221 self.assertEquals(cmd.undef, ['one', 'two'])
222
223 # make sure swig_opts is turned into a list
224 cmd = build_ext(dist)
225 cmd.swig_opts = None
226 cmd.finalize_options()
227 self.assertEquals(cmd.swig_opts, [])
228
229 cmd = build_ext(dist)
230 cmd.swig_opts = '1 2'
231 cmd.finalize_options()
232 self.assertEquals(cmd.swig_opts, ['1', '2'])
233
234 def test_check_extensions_list(self):
235 dist = Distribution()
236 cmd = build_ext(dist)
237 cmd.finalize_options()
238
239 #'extensions' option must be a list of Extension instances
240 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
241
242 # each element of 'ext_modules' option must be an
243 # Extension instance or 2-tuple
244 exts = [('bar', 'foo', 'bar'), 'foo']
245 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
246
247 # first element of each tuple in 'ext_modules'
248 # must be the extension name (a string) and match
249 # a python dotted-separated name
250 exts = [('foo-bar', '')]
251 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
252
253 # second element of each tuple in 'ext_modules'
254 # must be a ary (build info)
255 exts = [('foo.bar', '')]
256 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
257
258 # ok this one should pass
259 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
260 'some': 'bar'})]
261 cmd.check_extensions_list(exts)
262 ext = exts[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000263 self.assertTrue(isinstance(ext, Extension))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000264
265 # check_extensions_list adds in ext the values passed
266 # when they are in ('include_dirs', 'library_dirs', 'libraries'
267 # 'extra_objects', 'extra_compile_args', 'extra_link_args')
268 self.assertEquals(ext.libraries, 'foo')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000269 self.assertTrue(not hasattr(ext, 'some'))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000270
271 # 'macros' element of build info dict must be 1- or 2-tuple
272 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
273 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
274 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
275
276 exts[0][1]['macros'] = [('1', '2'), ('3',)]
277 cmd.check_extensions_list(exts)
278 self.assertEquals(exts[0].undef_macros, ['3'])
279 self.assertEquals(exts[0].define_macros, [('1', '2')])
280
281 def test_get_source_files(self):
282 modules = [Extension('foo', ['xxx'], optional=False)]
283 dist = Distribution({'name': 'xx', 'ext_modules': modules})
284 cmd = build_ext(dist)
285 cmd.ensure_finalized()
286 self.assertEquals(cmd.get_source_files(), ['xxx'])
287
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000288 def test_compiler_option(self):
289 # cmd.compiler is an option and
290 # should not be overriden by a compiler instance
291 # when the command is run
292 dist = Distribution()
293 cmd = build_ext(dist)
294 cmd.compiler = 'unix'
295 cmd.ensure_finalized()
296 cmd.run()
297 self.assertEquals(cmd.compiler, 'unix')
298
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000299 def test_get_outputs(self):
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000300 tmp_dir = self.mkdtemp()
301 c_file = os.path.join(tmp_dir, 'foo.c')
Tarek Ziadé4e3533e2009-05-13 22:20:49 +0000302 self.write_file(c_file, 'void initfoo(void) {};\n')
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000303 ext = Extension('foo', [c_file], optional=False)
304 dist = Distribution({'name': 'xx',
305 'ext_modules': [ext]})
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000306 cmd = build_ext(dist)
307 cmd.ensure_finalized()
308 self.assertEquals(len(cmd.get_outputs()), 1)
309
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000310 if os.name == "nt":
311 cmd.debug = sys.executable.endswith("_d.exe")
312
313 cmd.build_lib = os.path.join(self.tmp_dir, 'build')
314 cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
315
316 # issue #5977 : distutils build_ext.get_outputs
317 # returns wrong result with --inplace
Tarek Ziadé4210c6e2009-05-14 20:20:47 +0000318 other_tmp_dir = os.path.realpath(self.mkdtemp())
319 old_wd = os.getcwd()
320 os.chdir(other_tmp_dir)
321 try:
322 cmd.inplace = 1
323 cmd.run()
324 so_file = cmd.get_outputs()[0]
325 finally:
326 os.chdir(old_wd)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000327 self.assertTrue(os.path.exists(so_file))
Tarek Ziadéd18a84e2009-05-18 08:07:46 +0000328 self.assertEquals(os.path.splitext(so_file)[-1],
329 sysconfig.get_config_var('SO'))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000330 so_dir = os.path.dirname(so_file)
Tarek Ziadé4210c6e2009-05-14 20:20:47 +0000331 self.assertEquals(so_dir, other_tmp_dir)
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000332
333 cmd.inplace = 0
334 cmd.run()
335 so_file = cmd.get_outputs()[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000336 self.assertTrue(os.path.exists(so_file))
Tarek Ziadéd18a84e2009-05-18 08:07:46 +0000337 self.assertEquals(os.path.splitext(so_file)[-1],
338 sysconfig.get_config_var('SO'))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000339 so_dir = os.path.dirname(so_file)
340 self.assertEquals(so_dir, cmd.build_lib)
341
Tarek Ziadé822eb842009-05-19 16:22:57 +0000342 # inplace = 0, cmd.package = 'bar'
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000343 build_py = cmd.get_finalized_command('build_py')
344 build_py.package_dir = {'': 'bar'}
Tarek Ziadé822eb842009-05-19 16:22:57 +0000345 path = cmd.get_ext_fullpath('foo')
Tarek Ziadé0156f912009-06-29 16:19:22 +0000346 # checking that the last directory is the build_dir
Tarek Ziadé822eb842009-05-19 16:22:57 +0000347 path = os.path.split(path)[0]
Tarek Ziadé0156f912009-06-29 16:19:22 +0000348 self.assertEquals(path, cmd.build_lib)
Tarek Ziadé822eb842009-05-19 16:22:57 +0000349
350 # inplace = 1, cmd.package = 'bar'
351 cmd.inplace = 1
352 other_tmp_dir = os.path.realpath(self.mkdtemp())
353 old_wd = os.getcwd()
354 os.chdir(other_tmp_dir)
355 try:
356 path = cmd.get_ext_fullpath('foo')
357 finally:
358 os.chdir(old_wd)
359 # checking that the last directory is bar
360 path = os.path.split(path)[0]
361 lastdir = os.path.split(path)[-1]
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000362 self.assertEquals(lastdir, 'bar')
Tarek Ziadé822eb842009-05-19 16:22:57 +0000363
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000364 def test_ext_fullpath(self):
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000365 ext = sysconfig.get_config_vars()['SO']
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000366 # building lxml.etree inplace
367 #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
368 #etree_ext = Extension('lxml.etree', [etree_c])
369 #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
370 dist = Distribution()
Tarek Ziadé0156f912009-06-29 16:19:22 +0000371 cmd = build_ext(dist)
372 cmd.inplace = 1
373 cmd.distribution.package_dir = {'': 'src'}
374 cmd.distribution.packages = ['lxml', 'lxml.html']
375 curdir = os.getcwd()
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000376 wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
Tarek Ziadé0156f912009-06-29 16:19:22 +0000377 path = cmd.get_ext_fullpath('lxml.etree')
378 self.assertEquals(wanted, path)
379
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000380 # building lxml.etree not inplace
381 cmd.inplace = 0
382 cmd.build_lib = os.path.join(curdir, 'tmpdir')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000383 wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000384 path = cmd.get_ext_fullpath('lxml.etree')
385 self.assertEquals(wanted, path)
386
387 # building twisted.runner.portmap not inplace
388 build_py = cmd.get_finalized_command('build_py')
389 build_py.package_dir = {}
390 cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
391 path = cmd.get_ext_fullpath('twisted.runner.portmap')
392 wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000393 'portmap' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000394 self.assertEquals(wanted, path)
395
396 # building twisted.runner.portmap inplace
397 cmd.inplace = 1
398 path = cmd.get_ext_fullpath('twisted.runner.portmap')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000399 wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000400 self.assertEquals(wanted, path)
401
Tarek Ziadédd07ebb2009-07-06 13:52:17 +0000402 def test_compiler_deprecation_warning(self):
403 dist = Distribution()
404 cmd = build_ext(dist)
405
Tarek Ziadé556934b2009-07-08 22:42:43 +0000406 class MyCompiler(object):
407 def do_something(self):
408 pass
409
Tarek Ziadédd07ebb2009-07-06 13:52:17 +0000410 with check_warnings() as w:
411 warnings.simplefilter("always")
Tarek Ziadé556934b2009-07-08 22:42:43 +0000412 cmd.compiler = MyCompiler()
Tarek Ziadédd07ebb2009-07-06 13:52:17 +0000413 self.assertEquals(len(w.warnings), 1)
414 cmd.compile = 'unix'
415 self.assertEquals(len(w.warnings), 1)
Tarek Ziadé556934b2009-07-08 22:42:43 +0000416 cmd.compiler = MyCompiler()
417 cmd.compiler.do_something()
418 # two more warnings genereated by the get
419 # and the set
420 self.assertEquals(len(w.warnings), 3)
Tarek Ziadédd07ebb2009-07-06 13:52:17 +0000421
Georg Brandlb533e262008-05-25 18:19:30 +0000422def test_suite():
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000423 src = _get_source_filename()
424 if not os.path.exists(src):
Georg Brandlb533e262008-05-25 18:19:30 +0000425 if support.verbose:
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000426 print('test_build_ext: Cannot find source code (test'
427 ' must run in python build dir)')
Georg Brandlb533e262008-05-25 18:19:30 +0000428 return unittest.TestSuite()
429 else: return unittest.makeSuite(BuildExtTestCase)
430
431if __name__ == '__main__':
432 support.run_unittest(test_suite())