blob: b7cdc20aa478db975279253eeba04ac641581044 [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
6
7from distutils.core import Extension, Distribution
8from distutils.command.build_ext import build_ext
Tarek Ziadé36797272010-07-22 12:50:05 +00009from distutils import sysconfig
Tarek Ziadéc1375d52009-02-14 14:35:51 +000010from distutils.tests.support import TempdirManager
Tarek Ziadéb2e36f12009-03-31 22:37:55 +000011from distutils.tests.support import LoggingSilencer
12from distutils.extension import Extension
Tarek Ziadé06fbee12009-05-10 10:34:01 +000013from distutils.errors import (UnknownFileError, DistutilsSetupError,
14 CompileError)
Georg Brandlb533e262008-05-25 18:19:30 +000015
16import unittest
17from test import support
18
Christian Heimes3e7e0692008-11-25 21:21:32 +000019# http://bugs.python.org/issue4373
20# Don't load the xx module more than once.
21ALREADY_TESTED = False
22
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +000023def _get_source_filename():
24 srcdir = sysconfig.get_config_var('srcdir')
25 return os.path.join(srcdir, 'Modules', 'xxmodule.c')
26
Tarek Ziadé36797272010-07-22 12:50:05 +000027class BuildExtTestCase(TempdirManager,
28 LoggingSilencer,
29 unittest.TestCase):
Georg Brandlb533e262008-05-25 18:19:30 +000030 def setUp(self):
31 # Create a simple test environment
32 # Note that we're making changes to sys.path
Tarek Ziadé38e3d512009-02-27 12:58:56 +000033 super(BuildExtTestCase, self).setUp()
Tarek Ziadéc1375d52009-02-14 14:35:51 +000034 self.tmp_dir = self.mkdtemp()
Tarek Ziadé36797272010-07-22 12:50:05 +000035 self.sys_path = sys.path, sys.path[:]
36 sys.path.append(self.tmp_dir)
37 shutil.copy(_get_source_filename(), self.tmp_dir)
Tarek Ziadé38e3d512009-02-27 12:58:56 +000038 if sys.version > "2.6":
39 import site
40 self.old_user_base = site.USER_BASE
41 site.USER_BASE = self.mkdtemp()
42 from distutils.command import build_ext
43 build_ext.USER_BASE = site.USER_BASE
Georg Brandlb533e262008-05-25 18:19:30 +000044
45 def test_build_ext(self):
Christian Heimes3e7e0692008-11-25 21:21:32 +000046 global ALREADY_TESTED
Georg Brandlb533e262008-05-25 18:19:30 +000047 xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
48 xx_ext = Extension('xx', [xx_c])
49 dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
50 dist.package_dir = self.tmp_dir
51 cmd = build_ext(dist)
Thomas Heller84b7f0c2008-05-26 11:51:44 +000052 if os.name == "nt":
53 # On Windows, we must build a debug version iff running
54 # a debug build of Python
55 cmd.debug = sys.executable.endswith("_d.exe")
Georg Brandlb533e262008-05-25 18:19:30 +000056 cmd.build_lib = self.tmp_dir
57 cmd.build_temp = self.tmp_dir
58
59 old_stdout = sys.stdout
60 if not support.verbose:
61 # silence compiler output
62 sys.stdout = StringIO()
63 try:
64 cmd.ensure_finalized()
65 cmd.run()
66 finally:
67 sys.stdout = old_stdout
68
Christian Heimes3e7e0692008-11-25 21:21:32 +000069 if ALREADY_TESTED:
70 return
71 else:
72 ALREADY_TESTED = True
73
Georg Brandlb533e262008-05-25 18:19:30 +000074 import xx
75
76 for attr in ('error', 'foo', 'new', 'roj'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000077 self.assertTrue(hasattr(xx, attr))
Georg Brandlb533e262008-05-25 18:19:30 +000078
79 self.assertEquals(xx.foo(2, 5), 7)
80 self.assertEquals(xx.foo(13,15), 28)
81 self.assertEquals(xx.new().demo(), None)
82 doc = 'This is a template module just for instruction.'
83 self.assertEquals(xx.__doc__, doc)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000084 self.assertTrue(isinstance(xx.Null(), xx.Null))
85 self.assertTrue(isinstance(xx.Str(), xx.Str))
Georg Brandlb533e262008-05-25 18:19:30 +000086
Tarek Ziadé36797272010-07-22 12:50:05 +000087 def tearDown(self):
88 # Get everything back to normal
89 support.unload('xx')
90 sys.path = self.sys_path[0]
91 sys.path[:] = self.sys_path[1]
92 if sys.version > "2.6":
93 import site
94 site.USER_BASE = self.old_user_base
95 from distutils.command import build_ext
96 build_ext.USER_BASE = self.old_user_base
97 super(BuildExtTestCase, self).tearDown()
98
Tarek Ziadé5874ef12009-02-05 22:56:14 +000099 def test_solaris_enable_shared(self):
100 dist = Distribution({'name': 'xx'})
101 cmd = build_ext(dist)
102 old = sys.platform
103
104 sys.platform = 'sunos' # fooling finalize_options
Tarek Ziadé36797272010-07-22 12:50:05 +0000105 from distutils.sysconfig import _config_vars
106 old_var = _config_vars.get('Py_ENABLE_SHARED')
107 _config_vars['Py_ENABLE_SHARED'] = 1
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000108 try:
109 cmd.ensure_finalized()
110 finally:
111 sys.platform = old
112 if old_var is None:
Tarek Ziadé36797272010-07-22 12:50:05 +0000113 del _config_vars['Py_ENABLE_SHARED']
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000114 else:
Tarek Ziadé36797272010-07-22 12:50:05 +0000115 _config_vars['Py_ENABLE_SHARED'] = old_var
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000116
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000117 # make sure we get some library dirs under solaris
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000118 self.assertTrue(len(cmd.library_dirs) > 0)
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000119
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000120 def test_user_site(self):
121 # site.USER_SITE was introduced in 2.6
122 if sys.version < '2.6':
123 return
124
125 import site
126 dist = Distribution({'name': 'xx'})
127 cmd = build_ext(dist)
128
Tarek Ziadébe720e02009-05-09 11:55:12 +0000129 # making sure the user option is there
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000130 options = [name for name, short, lable in
131 cmd.user_options]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000132 self.assertTrue('user' in options)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000133
134 # setting a value
135 cmd.user = 1
136
137 # setting user based lib and include
138 lib = os.path.join(site.USER_BASE, 'lib')
139 incl = os.path.join(site.USER_BASE, 'include')
140 os.mkdir(lib)
141 os.mkdir(incl)
142
143 # let's run finalize
144 cmd.ensure_finalized()
145
146 # see if include_dirs and library_dirs
147 # were set
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000148 self.assertTrue(lib in cmd.library_dirs)
149 self.assertTrue(lib in cmd.rpath)
150 self.assertTrue(incl in cmd.include_dirs)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000151
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000152 def test_optional_extension(self):
153
154 # this extension will fail, but let's ignore this failure
155 # with the optional argument.
156 modules = [Extension('foo', ['xxx'], optional=False)]
157 dist = Distribution({'name': 'xx', 'ext_modules': modules})
158 cmd = build_ext(dist)
159 cmd.ensure_finalized()
Tarek Ziadé30911292009-03-31 22:50:54 +0000160 self.assertRaises((UnknownFileError, CompileError),
161 cmd.run) # should raise an error
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000162
163 modules = [Extension('foo', ['xxx'], optional=True)]
164 dist = Distribution({'name': 'xx', 'ext_modules': modules})
165 cmd = build_ext(dist)
166 cmd.ensure_finalized()
167 cmd.run() # should pass
168
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000169 def test_finalize_options(self):
170 # Make sure Python's include directories (for Python.h, pyconfig.h,
171 # etc.) are in the include search path.
172 modules = [Extension('foo', ['xxx'], optional=False)]
173 dist = Distribution({'name': 'xx', 'ext_modules': modules})
174 cmd = build_ext(dist)
175 cmd.finalize_options()
176
Tarek Ziadé36797272010-07-22 12:50:05 +0000177 from distutils import sysconfig
178 py_include = sysconfig.get_python_inc()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000179 self.assertTrue(py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000180
Tarek Ziadé36797272010-07-22 12:50:05 +0000181 plat_py_include = sysconfig.get_python_inc(plat_specific=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000182 self.assertTrue(plat_py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000183
184 # make sure cmd.libraries is turned into a list
185 # if it's a string
186 cmd = build_ext(dist)
187 cmd.libraries = 'my_lib'
188 cmd.finalize_options()
189 self.assertEquals(cmd.libraries, ['my_lib'])
190
191 # make sure cmd.library_dirs is turned into a list
192 # if it's a string
193 cmd = build_ext(dist)
194 cmd.library_dirs = 'my_lib_dir'
195 cmd.finalize_options()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000196 self.assertTrue('my_lib_dir' in cmd.library_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000197
198 # make sure rpath is turned into a list
199 # if it's a list of os.pathsep's paths
200 cmd = build_ext(dist)
201 cmd.rpath = os.pathsep.join(['one', 'two'])
202 cmd.finalize_options()
203 self.assertEquals(cmd.rpath, ['one', 'two'])
204
205 # XXX more tests to perform for win32
206
207 # make sure define is turned into 2-tuples
208 # strings if they are ','-separated strings
209 cmd = build_ext(dist)
210 cmd.define = 'one,two'
211 cmd.finalize_options()
212 self.assertEquals(cmd.define, [('one', '1'), ('two', '1')])
213
214 # make sure undef is turned into a list of
215 # strings if they are ','-separated strings
216 cmd = build_ext(dist)
217 cmd.undef = 'one,two'
218 cmd.finalize_options()
219 self.assertEquals(cmd.undef, ['one', 'two'])
220
221 # make sure swig_opts is turned into a list
222 cmd = build_ext(dist)
223 cmd.swig_opts = None
224 cmd.finalize_options()
225 self.assertEquals(cmd.swig_opts, [])
226
227 cmd = build_ext(dist)
228 cmd.swig_opts = '1 2'
229 cmd.finalize_options()
230 self.assertEquals(cmd.swig_opts, ['1', '2'])
231
232 def test_check_extensions_list(self):
233 dist = Distribution()
234 cmd = build_ext(dist)
235 cmd.finalize_options()
236
237 #'extensions' option must be a list of Extension instances
238 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
239
240 # each element of 'ext_modules' option must be an
241 # Extension instance or 2-tuple
242 exts = [('bar', 'foo', 'bar'), 'foo']
243 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
244
245 # first element of each tuple in 'ext_modules'
246 # must be the extension name (a string) and match
247 # a python dotted-separated name
248 exts = [('foo-bar', '')]
249 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
250
251 # second element of each tuple in 'ext_modules'
252 # must be a ary (build info)
253 exts = [('foo.bar', '')]
254 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
255
256 # ok this one should pass
257 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
258 'some': 'bar'})]
259 cmd.check_extensions_list(exts)
260 ext = exts[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000261 self.assertTrue(isinstance(ext, Extension))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000262
263 # check_extensions_list adds in ext the values passed
264 # when they are in ('include_dirs', 'library_dirs', 'libraries'
265 # 'extra_objects', 'extra_compile_args', 'extra_link_args')
266 self.assertEquals(ext.libraries, 'foo')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000267 self.assertTrue(not hasattr(ext, 'some'))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000268
269 # 'macros' element of build info dict must be 1- or 2-tuple
270 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
271 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
272 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
273
274 exts[0][1]['macros'] = [('1', '2'), ('3',)]
275 cmd.check_extensions_list(exts)
276 self.assertEquals(exts[0].undef_macros, ['3'])
277 self.assertEquals(exts[0].define_macros, [('1', '2')])
278
279 def test_get_source_files(self):
280 modules = [Extension('foo', ['xxx'], optional=False)]
281 dist = Distribution({'name': 'xx', 'ext_modules': modules})
282 cmd = build_ext(dist)
283 cmd.ensure_finalized()
284 self.assertEquals(cmd.get_source_files(), ['xxx'])
285
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000286 def test_compiler_option(self):
287 # cmd.compiler is an option and
288 # should not be overriden by a compiler instance
289 # when the command is run
290 dist = Distribution()
291 cmd = build_ext(dist)
292 cmd.compiler = 'unix'
293 cmd.ensure_finalized()
294 cmd.run()
295 self.assertEquals(cmd.compiler, 'unix')
296
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000297 def test_get_outputs(self):
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000298 tmp_dir = self.mkdtemp()
299 c_file = os.path.join(tmp_dir, 'foo.c')
Tarek Ziadé6504c662009-07-11 10:59:56 +0000300 self.write_file(c_file, 'void PyInit_foo(void) {};\n')
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000301 ext = Extension('foo', [c_file], optional=False)
302 dist = Distribution({'name': 'xx',
303 'ext_modules': [ext]})
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000304 cmd = build_ext(dist)
305 cmd.ensure_finalized()
306 self.assertEquals(len(cmd.get_outputs()), 1)
307
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000308 if os.name == "nt":
309 cmd.debug = sys.executable.endswith("_d.exe")
310
311 cmd.build_lib = os.path.join(self.tmp_dir, 'build')
312 cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
313
314 # issue #5977 : distutils build_ext.get_outputs
315 # returns wrong result with --inplace
Tarek Ziadé4210c6e2009-05-14 20:20:47 +0000316 other_tmp_dir = os.path.realpath(self.mkdtemp())
317 old_wd = os.getcwd()
318 os.chdir(other_tmp_dir)
319 try:
320 cmd.inplace = 1
321 cmd.run()
322 so_file = cmd.get_outputs()[0]
323 finally:
324 os.chdir(old_wd)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000325 self.assertTrue(os.path.exists(so_file))
Tarek Ziadéd18a84e2009-05-18 08:07:46 +0000326 self.assertEquals(os.path.splitext(so_file)[-1],
327 sysconfig.get_config_var('SO'))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000328 so_dir = os.path.dirname(so_file)
Tarek Ziadé4210c6e2009-05-14 20:20:47 +0000329 self.assertEquals(so_dir, other_tmp_dir)
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000330
331 cmd.inplace = 0
Tarek Ziadé36797272010-07-22 12:50:05 +0000332 cmd.compiler = None
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000333 cmd.run()
334 so_file = cmd.get_outputs()[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000335 self.assertTrue(os.path.exists(so_file))
Tarek Ziadéd18a84e2009-05-18 08:07:46 +0000336 self.assertEquals(os.path.splitext(so_file)[-1],
337 sysconfig.get_config_var('SO'))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000338 so_dir = os.path.dirname(so_file)
339 self.assertEquals(so_dir, cmd.build_lib)
340
Tarek Ziadé822eb842009-05-19 16:22:57 +0000341 # inplace = 0, cmd.package = 'bar'
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000342 build_py = cmd.get_finalized_command('build_py')
343 build_py.package_dir = {'': 'bar'}
Tarek Ziadé822eb842009-05-19 16:22:57 +0000344 path = cmd.get_ext_fullpath('foo')
Tarek Ziadé0156f912009-06-29 16:19:22 +0000345 # checking that the last directory is the build_dir
Tarek Ziadé822eb842009-05-19 16:22:57 +0000346 path = os.path.split(path)[0]
Tarek Ziadé0156f912009-06-29 16:19:22 +0000347 self.assertEquals(path, cmd.build_lib)
Tarek Ziadé822eb842009-05-19 16:22:57 +0000348
349 # inplace = 1, cmd.package = 'bar'
350 cmd.inplace = 1
351 other_tmp_dir = os.path.realpath(self.mkdtemp())
352 old_wd = os.getcwd()
353 os.chdir(other_tmp_dir)
354 try:
355 path = cmd.get_ext_fullpath('foo')
356 finally:
357 os.chdir(old_wd)
358 # checking that the last directory is bar
359 path = os.path.split(path)[0]
360 lastdir = os.path.split(path)[-1]
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000361 self.assertEquals(lastdir, 'bar')
Tarek Ziadé822eb842009-05-19 16:22:57 +0000362
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000363 def test_ext_fullpath(self):
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000364 ext = sysconfig.get_config_vars()['SO']
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000365 # building lxml.etree inplace
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 dist = Distribution()
Tarek Ziadé0156f912009-06-29 16:19:22 +0000370 cmd = build_ext(dist)
371 cmd.inplace = 1
372 cmd.distribution.package_dir = {'': 'src'}
373 cmd.distribution.packages = ['lxml', 'lxml.html']
374 curdir = os.getcwd()
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000375 wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
Tarek Ziadé0156f912009-06-29 16:19:22 +0000376 path = cmd.get_ext_fullpath('lxml.etree')
377 self.assertEquals(wanted, path)
378
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000379 # building lxml.etree not inplace
380 cmd.inplace = 0
381 cmd.build_lib = os.path.join(curdir, 'tmpdir')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000382 wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000383 path = cmd.get_ext_fullpath('lxml.etree')
384 self.assertEquals(wanted, path)
385
386 # building twisted.runner.portmap not inplace
387 build_py = cmd.get_finalized_command('build_py')
388 build_py.package_dir = {}
389 cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
390 path = cmd.get_ext_fullpath('twisted.runner.portmap')
391 wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000392 'portmap' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000393 self.assertEquals(wanted, path)
394
395 # building twisted.runner.portmap inplace
396 cmd.inplace = 1
397 path = cmd.get_ext_fullpath('twisted.runner.portmap')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000398 wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000399 self.assertEquals(wanted, path)
400
Georg Brandlb533e262008-05-25 18:19:30 +0000401def test_suite():
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000402 src = _get_source_filename()
403 if not os.path.exists(src):
Georg Brandlb533e262008-05-25 18:19:30 +0000404 if support.verbose:
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000405 print('test_build_ext: Cannot find source code (test'
406 ' must run in python build dir)')
Georg Brandlb533e262008-05-25 18:19:30 +0000407 return unittest.TestSuite()
408 else: return unittest.makeSuite(BuildExtTestCase)
409
410if __name__ == '__main__':
411 support.run_unittest(test_suite())