blob: 0ce7f0f81c888deef111e8e5e5d4d7b2f21e0d86 [file] [log] [blame]
Georg Brandlb533e262008-05-25 18:19:30 +00001import sys
2import os
Georg Brandlb533e262008-05-25 18:19:30 +00003import shutil
4from io import StringIO
Ronald Oussoren222e89a2011-05-15 16:46:11 +02005import textwrap
Georg Brandlb533e262008-05-25 18:19:30 +00006
Barry Warsaw8cf4eae2010-10-16 01:04:07 +00007from distutils.core import Distribution
Georg Brandlb533e262008-05-25 18:19:30 +00008from 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
Barry Warsaw8cf4eae2010-10-16 01:04:07 +000013from distutils.errors import (
Ned Deilyd13007f2011-06-28 19:43:15 -070014 CompileError, DistutilsPlatformError, DistutilsSetupError,
15 UnknownFileError)
Georg Brandlb533e262008-05-25 18:19:30 +000016
17import unittest
18from test import support
Éric Araujo70ec44a2010-11-06 02:44:43 +000019from test.support import run_unittest
Georg Brandlb533e262008-05-25 18:19:30 +000020
Christian Heimes3e7e0692008-11-25 21:21:32 +000021# http://bugs.python.org/issue4373
22# Don't load the xx module more than once.
23ALREADY_TESTED = False
24
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +000025def _get_source_filename():
Ned Deily58f27b22011-06-28 00:42:50 -070026 # use installed copy if available
27 tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c')
28 if os.path.exists(tests_f):
29 return tests_f
30 # otherwise try using copy from build directory
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +000031 srcdir = sysconfig.get_config_var('srcdir')
32 return os.path.join(srcdir, 'Modules', 'xxmodule.c')
33
Tarek Ziadé36797272010-07-22 12:50:05 +000034class BuildExtTestCase(TempdirManager,
35 LoggingSilencer,
36 unittest.TestCase):
Georg Brandlb533e262008-05-25 18:19:30 +000037 def setUp(self):
38 # Create a simple test environment
39 # Note that we're making changes to sys.path
Tarek Ziadé38e3d512009-02-27 12:58:56 +000040 super(BuildExtTestCase, self).setUp()
Tarek Ziadéc1375d52009-02-14 14:35:51 +000041 self.tmp_dir = self.mkdtemp()
Tarek Ziadé36797272010-07-22 12:50:05 +000042 self.sys_path = sys.path, sys.path[:]
43 sys.path.append(self.tmp_dir)
Ned Deily58f27b22011-06-28 00:42:50 -070044 filename = _get_source_filename()
45 if os.path.exists(filename):
46 shutil.copy(filename, self.tmp_dir)
Tarek Ziadé38e3d512009-02-27 12:58:56 +000047 if sys.version > "2.6":
48 import site
49 self.old_user_base = site.USER_BASE
50 site.USER_BASE = self.mkdtemp()
51 from distutils.command import build_ext
52 build_ext.USER_BASE = site.USER_BASE
Georg Brandlb533e262008-05-25 18:19:30 +000053
Barry Warsaw8cf4eae2010-10-16 01:04:07 +000054 def _fixup_command(self, cmd):
55 # When Python was build with --enable-shared, -L. is not good enough
56 # to find the libpython<blah>.so. This is because regrtest runs it
57 # under a tempdir, not in the top level where the .so lives. By the
58 # time we've gotten here, Python's already been chdir'd to the
59 # tempdir.
60 #
61 # To further add to the fun, we can't just add library_dirs to the
62 # Extension() instance because that doesn't get plumbed through to the
63 # final compiler command.
Barry Warsaw4ebfdf02010-10-22 17:17:51 +000064 if (sysconfig.get_config_var('Py_ENABLE_SHARED') and
65 not sys.platform.startswith('win')):
Éric Araujo68fc9aa2010-10-21 23:02:07 +000066 runshared = sysconfig.get_config_var('RUNSHARED')
67 if runshared is None:
68 cmd.library_dirs = ['.']
69 else:
70 name, equals, value = runshared.partition('=')
71 cmd.library_dirs = value.split(os.pathsep)
Barry Warsaw8cf4eae2010-10-16 01:04:07 +000072
Georg Brandlb533e262008-05-25 18:19:30 +000073 def test_build_ext(self):
Christian Heimes3e7e0692008-11-25 21:21:32 +000074 global ALREADY_TESTED
Georg Brandlb533e262008-05-25 18:19:30 +000075 xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
Ned Deily58f27b22011-06-28 00:42:50 -070076 if not os.path.exists(xx_c):
77 return
Georg Brandlb533e262008-05-25 18:19:30 +000078 xx_ext = Extension('xx', [xx_c])
79 dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
80 dist.package_dir = self.tmp_dir
81 cmd = build_ext(dist)
Barry Warsaw8cf4eae2010-10-16 01:04:07 +000082 self._fixup_command(cmd)
Thomas Heller84b7f0c2008-05-26 11:51:44 +000083 if os.name == "nt":
84 # On Windows, we must build a debug version iff running
85 # a debug build of Python
86 cmd.debug = sys.executable.endswith("_d.exe")
Georg Brandlb533e262008-05-25 18:19:30 +000087 cmd.build_lib = self.tmp_dir
88 cmd.build_temp = self.tmp_dir
89
90 old_stdout = sys.stdout
91 if not support.verbose:
92 # silence compiler output
93 sys.stdout = StringIO()
94 try:
95 cmd.ensure_finalized()
96 cmd.run()
97 finally:
98 sys.stdout = old_stdout
99
Christian Heimes3e7e0692008-11-25 21:21:32 +0000100 if ALREADY_TESTED:
101 return
102 else:
103 ALREADY_TESTED = True
104
Georg Brandlb533e262008-05-25 18:19:30 +0000105 import xx
106
107 for attr in ('error', 'foo', 'new', 'roj'):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000108 self.assertTrue(hasattr(xx, attr))
Georg Brandlb533e262008-05-25 18:19:30 +0000109
Ezio Melottib3aedd42010-11-20 19:04:17 +0000110 self.assertEqual(xx.foo(2, 5), 7)
111 self.assertEqual(xx.foo(13,15), 28)
112 self.assertEqual(xx.new().demo(), None)
Georg Brandlb533e262008-05-25 18:19:30 +0000113 doc = 'This is a template module just for instruction.'
Ezio Melottib3aedd42010-11-20 19:04:17 +0000114 self.assertEqual(xx.__doc__, doc)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000115 self.assertTrue(isinstance(xx.Null(), xx.Null))
116 self.assertTrue(isinstance(xx.Str(), xx.Str))
Georg Brandlb533e262008-05-25 18:19:30 +0000117
Tarek Ziadé36797272010-07-22 12:50:05 +0000118 def tearDown(self):
119 # Get everything back to normal
120 support.unload('xx')
121 sys.path = self.sys_path[0]
122 sys.path[:] = self.sys_path[1]
123 if sys.version > "2.6":
124 import site
125 site.USER_BASE = self.old_user_base
126 from distutils.command import build_ext
127 build_ext.USER_BASE = self.old_user_base
128 super(BuildExtTestCase, self).tearDown()
129
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000130 def test_solaris_enable_shared(self):
131 dist = Distribution({'name': 'xx'})
132 cmd = build_ext(dist)
133 old = sys.platform
134
135 sys.platform = 'sunos' # fooling finalize_options
Tarek Ziadé36797272010-07-22 12:50:05 +0000136 from distutils.sysconfig import _config_vars
137 old_var = _config_vars.get('Py_ENABLE_SHARED')
138 _config_vars['Py_ENABLE_SHARED'] = 1
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000139 try:
140 cmd.ensure_finalized()
141 finally:
142 sys.platform = old
143 if old_var is None:
Tarek Ziadé36797272010-07-22 12:50:05 +0000144 del _config_vars['Py_ENABLE_SHARED']
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000145 else:
Tarek Ziadé36797272010-07-22 12:50:05 +0000146 _config_vars['Py_ENABLE_SHARED'] = old_var
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000147
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000148 # make sure we get some library dirs under solaris
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000149 self.assertTrue(len(cmd.library_dirs) > 0)
Tarek Ziadé5874ef12009-02-05 22:56:14 +0000150
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000151 def test_user_site(self):
152 # site.USER_SITE was introduced in 2.6
153 if sys.version < '2.6':
154 return
155
156 import site
157 dist = Distribution({'name': 'xx'})
158 cmd = build_ext(dist)
159
Tarek Ziadébe720e02009-05-09 11:55:12 +0000160 # making sure the user option is there
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000161 options = [name for name, short, lable in
162 cmd.user_options]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000163 self.assertTrue('user' in options)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000164
165 # setting a value
166 cmd.user = 1
167
168 # setting user based lib and include
169 lib = os.path.join(site.USER_BASE, 'lib')
170 incl = os.path.join(site.USER_BASE, 'include')
171 os.mkdir(lib)
172 os.mkdir(incl)
173
174 # let's run finalize
175 cmd.ensure_finalized()
176
177 # see if include_dirs and library_dirs
178 # were set
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000179 self.assertTrue(lib in cmd.library_dirs)
180 self.assertTrue(lib in cmd.rpath)
181 self.assertTrue(incl in cmd.include_dirs)
Tarek Ziadé38e3d512009-02-27 12:58:56 +0000182
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000183 def test_optional_extension(self):
184
185 # this extension will fail, but let's ignore this failure
186 # with the optional argument.
187 modules = [Extension('foo', ['xxx'], optional=False)]
188 dist = Distribution({'name': 'xx', 'ext_modules': modules})
189 cmd = build_ext(dist)
190 cmd.ensure_finalized()
Tarek Ziadé30911292009-03-31 22:50:54 +0000191 self.assertRaises((UnknownFileError, CompileError),
192 cmd.run) # should raise an error
Tarek Ziadéb2e36f12009-03-31 22:37:55 +0000193
194 modules = [Extension('foo', ['xxx'], optional=True)]
195 dist = Distribution({'name': 'xx', 'ext_modules': modules})
196 cmd = build_ext(dist)
197 cmd.ensure_finalized()
198 cmd.run() # should pass
199
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000200 def test_finalize_options(self):
201 # Make sure Python's include directories (for Python.h, pyconfig.h,
202 # etc.) are in the include search path.
203 modules = [Extension('foo', ['xxx'], optional=False)]
204 dist = Distribution({'name': 'xx', 'ext_modules': modules})
205 cmd = build_ext(dist)
206 cmd.finalize_options()
207
Tarek Ziadé36797272010-07-22 12:50:05 +0000208 from distutils import sysconfig
209 py_include = sysconfig.get_python_inc()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000210 self.assertTrue(py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000211
Tarek Ziadé36797272010-07-22 12:50:05 +0000212 plat_py_include = sysconfig.get_python_inc(plat_specific=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000213 self.assertTrue(plat_py_include in cmd.include_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000214
215 # make sure cmd.libraries is turned into a list
216 # if it's a string
217 cmd = build_ext(dist)
218 cmd.libraries = 'my_lib'
219 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000220 self.assertEqual(cmd.libraries, ['my_lib'])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000221
222 # make sure cmd.library_dirs is turned into a list
223 # if it's a string
224 cmd = build_ext(dist)
225 cmd.library_dirs = 'my_lib_dir'
226 cmd.finalize_options()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000227 self.assertTrue('my_lib_dir' in cmd.library_dirs)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000228
229 # make sure rpath is turned into a list
230 # if it's a list of os.pathsep's paths
231 cmd = build_ext(dist)
232 cmd.rpath = os.pathsep.join(['one', 'two'])
233 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000234 self.assertEqual(cmd.rpath, ['one', 'two'])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000235
236 # XXX more tests to perform for win32
237
238 # make sure define is turned into 2-tuples
239 # strings if they are ','-separated strings
240 cmd = build_ext(dist)
241 cmd.define = 'one,two'
242 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000243 self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000244
245 # make sure undef is turned into a list of
246 # strings if they are ','-separated strings
247 cmd = build_ext(dist)
248 cmd.undef = 'one,two'
249 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000250 self.assertEqual(cmd.undef, ['one', 'two'])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000251
252 # make sure swig_opts is turned into a list
253 cmd = build_ext(dist)
254 cmd.swig_opts = None
255 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000256 self.assertEqual(cmd.swig_opts, [])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000257
258 cmd = build_ext(dist)
259 cmd.swig_opts = '1 2'
260 cmd.finalize_options()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000261 self.assertEqual(cmd.swig_opts, ['1', '2'])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000262
263 def test_check_extensions_list(self):
264 dist = Distribution()
265 cmd = build_ext(dist)
266 cmd.finalize_options()
267
268 #'extensions' option must be a list of Extension instances
Barry Warsaw8cf4eae2010-10-16 01:04:07 +0000269 self.assertRaises(DistutilsSetupError,
270 cmd.check_extensions_list, 'foo')
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000271
272 # each element of 'ext_modules' option must be an
273 # Extension instance or 2-tuple
274 exts = [('bar', 'foo', 'bar'), 'foo']
275 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
276
277 # first element of each tuple in 'ext_modules'
278 # must be the extension name (a string) and match
279 # a python dotted-separated name
280 exts = [('foo-bar', '')]
281 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
282
283 # second element of each tuple in 'ext_modules'
284 # must be a ary (build info)
285 exts = [('foo.bar', '')]
286 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
287
288 # ok this one should pass
289 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
290 'some': 'bar'})]
291 cmd.check_extensions_list(exts)
292 ext = exts[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000293 self.assertTrue(isinstance(ext, Extension))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000294
295 # check_extensions_list adds in ext the values passed
296 # when they are in ('include_dirs', 'library_dirs', 'libraries'
297 # 'extra_objects', 'extra_compile_args', 'extra_link_args')
Ezio Melottib3aedd42010-11-20 19:04:17 +0000298 self.assertEqual(ext.libraries, 'foo')
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000299 self.assertTrue(not hasattr(ext, 'some'))
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000300
301 # 'macros' element of build info dict must be 1- or 2-tuple
302 exts = [('foo.bar', {'sources': [''], 'libraries': 'foo',
303 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})]
304 self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
305
306 exts[0][1]['macros'] = [('1', '2'), ('3',)]
307 cmd.check_extensions_list(exts)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000308 self.assertEqual(exts[0].undef_macros, ['3'])
309 self.assertEqual(exts[0].define_macros, [('1', '2')])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000310
311 def test_get_source_files(self):
312 modules = [Extension('foo', ['xxx'], optional=False)]
313 dist = Distribution({'name': 'xx', 'ext_modules': modules})
314 cmd = build_ext(dist)
315 cmd.ensure_finalized()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000316 self.assertEqual(cmd.get_source_files(), ['xxx'])
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000317
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000318 def test_compiler_option(self):
319 # cmd.compiler is an option and
320 # should not be overriden by a compiler instance
321 # when the command is run
322 dist = Distribution()
323 cmd = build_ext(dist)
324 cmd.compiler = 'unix'
325 cmd.ensure_finalized()
326 cmd.run()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000327 self.assertEqual(cmd.compiler, 'unix')
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000328
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000329 def test_get_outputs(self):
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000330 tmp_dir = self.mkdtemp()
331 c_file = os.path.join(tmp_dir, 'foo.c')
Victor Stinner3e2b7172010-11-09 09:32:19 +0000332 self.write_file(c_file, 'void PyInit_foo(void) {}\n')
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000333 ext = Extension('foo', [c_file], optional=False)
334 dist = Distribution({'name': 'xx',
335 'ext_modules': [ext]})
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000336 cmd = build_ext(dist)
Barry Warsaw8cf4eae2010-10-16 01:04:07 +0000337 self._fixup_command(cmd)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000338 cmd.ensure_finalized()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000339 self.assertEqual(len(cmd.get_outputs()), 1)
Tarek Ziadé06fbee12009-05-10 10:34:01 +0000340
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000341 if os.name == "nt":
342 cmd.debug = sys.executable.endswith("_d.exe")
343
344 cmd.build_lib = os.path.join(self.tmp_dir, 'build')
345 cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
346
347 # issue #5977 : distutils build_ext.get_outputs
348 # returns wrong result with --inplace
Tarek Ziadé4210c6e2009-05-14 20:20:47 +0000349 other_tmp_dir = os.path.realpath(self.mkdtemp())
350 old_wd = os.getcwd()
351 os.chdir(other_tmp_dir)
352 try:
353 cmd.inplace = 1
354 cmd.run()
355 so_file = cmd.get_outputs()[0]
356 finally:
357 os.chdir(old_wd)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000358 self.assertTrue(os.path.exists(so_file))
Barry Warsaw35f3a2c2010-09-03 18:30:30 +0000359 so_ext = sysconfig.get_config_var('SO')
360 self.assertTrue(so_file.endswith(so_ext))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000361 so_dir = os.path.dirname(so_file)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000362 self.assertEqual(so_dir, other_tmp_dir)
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000363
364 cmd.inplace = 0
Tarek Ziadé36797272010-07-22 12:50:05 +0000365 cmd.compiler = None
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000366 cmd.run()
367 so_file = cmd.get_outputs()[0]
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000368 self.assertTrue(os.path.exists(so_file))
Barry Warsaw35f3a2c2010-09-03 18:30:30 +0000369 self.assertTrue(so_file.endswith(so_ext))
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000370 so_dir = os.path.dirname(so_file)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000371 self.assertEqual(so_dir, cmd.build_lib)
Tarek Ziadéff0e5002009-05-12 17:14:01 +0000372
Tarek Ziadé822eb842009-05-19 16:22:57 +0000373 # inplace = 0, cmd.package = 'bar'
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000374 build_py = cmd.get_finalized_command('build_py')
375 build_py.package_dir = {'': 'bar'}
Tarek Ziadé822eb842009-05-19 16:22:57 +0000376 path = cmd.get_ext_fullpath('foo')
Tarek Ziadé0156f912009-06-29 16:19:22 +0000377 # checking that the last directory is the build_dir
Tarek Ziadé822eb842009-05-19 16:22:57 +0000378 path = os.path.split(path)[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000379 self.assertEqual(path, cmd.build_lib)
Tarek Ziadé822eb842009-05-19 16:22:57 +0000380
381 # inplace = 1, cmd.package = 'bar'
382 cmd.inplace = 1
383 other_tmp_dir = os.path.realpath(self.mkdtemp())
384 old_wd = os.getcwd()
385 os.chdir(other_tmp_dir)
386 try:
387 path = cmd.get_ext_fullpath('foo')
388 finally:
389 os.chdir(old_wd)
390 # checking that the last directory is bar
391 path = os.path.split(path)[0]
392 lastdir = os.path.split(path)[-1]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000393 self.assertEqual(lastdir, 'bar')
Tarek Ziadé822eb842009-05-19 16:22:57 +0000394
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000395 def test_ext_fullpath(self):
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000396 ext = sysconfig.get_config_vars()['SO']
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000397 # building lxml.etree inplace
398 #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
399 #etree_ext = Extension('lxml.etree', [etree_c])
400 #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
401 dist = Distribution()
Tarek Ziadé0156f912009-06-29 16:19:22 +0000402 cmd = build_ext(dist)
403 cmd.inplace = 1
404 cmd.distribution.package_dir = {'': 'src'}
405 cmd.distribution.packages = ['lxml', 'lxml.html']
406 curdir = os.getcwd()
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000407 wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
Tarek Ziadé0156f912009-06-29 16:19:22 +0000408 path = cmd.get_ext_fullpath('lxml.etree')
Ezio Melottib3aedd42010-11-20 19:04:17 +0000409 self.assertEqual(wanted, path)
Tarek Ziadé0156f912009-06-29 16:19:22 +0000410
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000411 # building lxml.etree not inplace
412 cmd.inplace = 0
413 cmd.build_lib = os.path.join(curdir, 'tmpdir')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000414 wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000415 path = cmd.get_ext_fullpath('lxml.etree')
Ezio Melottib3aedd42010-11-20 19:04:17 +0000416 self.assertEqual(wanted, path)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000417
418 # building twisted.runner.portmap not inplace
419 build_py = cmd.get_finalized_command('build_py')
420 build_py.package_dir = {}
421 cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
422 path = cmd.get_ext_fullpath('twisted.runner.portmap')
423 wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000424 'portmap' + ext)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000425 self.assertEqual(wanted, path)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000426
427 # building twisted.runner.portmap inplace
428 cmd.inplace = 1
429 path = cmd.get_ext_fullpath('twisted.runner.portmap')
Tarek Ziadéb7815e32009-07-10 09:14:31 +0000430 wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000431 self.assertEqual(wanted, path)
Tarek Ziadée10d6de2009-07-03 08:33:28 +0000432
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200433
434 @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
Ned Deilyd13007f2011-06-28 19:43:15 -0700435 def test_deployment_target_default(self):
436 # Issue 9516: Test that, in the absence of the environment variable,
437 # an extension module is compiled with the same deployment target as
438 # the interpreter.
439 self._try_compile_deployment_target('==', None)
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200440
Ned Deilyd13007f2011-06-28 19:43:15 -0700441 @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
442 def test_deployment_target_too_low(self):
443 # Issue 9516: Test that an extension module is not allowed to be
444 # compiled with a deployment target less than that of the interpreter.
445 self.assertRaises(DistutilsPlatformError,
446 self._try_compile_deployment_target, '>', '10.1')
447
448 @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
449 def test_deployment_target_higher_ok(self):
450 # Issue 9516: Test that an extension module can be compiled with a
451 # deployment target higher than that of the interpreter: the ext
452 # module may depend on some newer OS feature.
453 deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
454 if deptarget:
455 # increment the minor version number (i.e. 10.6 -> 10.7)
456 deptarget = [int(x) for x in deptarget.split('.')]
457 deptarget[-1] += 1
458 deptarget = '.'.join(str(i) for i in deptarget)
459 self._try_compile_deployment_target('<', deptarget)
460
461 def _try_compile_deployment_target(self, operator, target):
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200462 orig_environ = os.environ
463 os.environ = orig_environ.copy()
464 self.addCleanup(setattr, os, 'environ', orig_environ)
465
Ned Deilyd13007f2011-06-28 19:43:15 -0700466 if target is None:
467 if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
468 del os.environ['MACOSX_DEPLOYMENT_TARGET']
469 else:
470 os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200471
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200472 deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c')
473
474 with open(deptarget_c, 'w') as fp:
475 fp.write(textwrap.dedent('''\
476 #include <AvailabilityMacros.h>
477
478 int dummy;
479
Ned Deilyd13007f2011-06-28 19:43:15 -0700480 #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED
481 #else
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200482 #error "Unexpected target"
483 #endif
484
Ned Deilyd13007f2011-06-28 19:43:15 -0700485 ''' % operator))
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200486
Ned Deilyd13007f2011-06-28 19:43:15 -0700487 # get the deployment target that the interpreter was built with
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200488 target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET')
489 target = tuple(map(int, target.split('.')))
490 target = '%02d%01d0' % target
Ronald Oussoren222e89a2011-05-15 16:46:11 +0200491 deptarget_ext = Extension(
492 'deptarget',
493 [deptarget_c],
494 extra_compile_args=['-DTARGET=%s'%(target,)],
495 )
496 dist = Distribution({
497 'name': 'deptarget',
498 'ext_modules': [deptarget_ext]
499 })
500 dist.package_dir = self.tmp_dir
501 cmd = build_ext(dist)
502 cmd.build_lib = self.tmp_dir
503 cmd.build_temp = self.tmp_dir
504
505 try:
506 old_stdout = sys.stdout
507 if not support.verbose:
508 # silence compiler output
509 sys.stdout = StringIO()
510 try:
511 cmd.ensure_finalized()
512 cmd.run()
513 finally:
514 sys.stdout = old_stdout
515
516 except CompileError:
517 self.fail("Wrong deployment target during compilation")
518
519
Georg Brandlb533e262008-05-25 18:19:30 +0000520def test_suite():
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000521 src = _get_source_filename()
522 if not os.path.exists(src):
Georg Brandlb533e262008-05-25 18:19:30 +0000523 if support.verbose:
Neil Schemenauerd8f63bb2009-02-06 21:42:05 +0000524 print('test_build_ext: Cannot find source code (test'
525 ' must run in python build dir)')
Georg Brandlb533e262008-05-25 18:19:30 +0000526 return unittest.TestSuite()
527 else: return unittest.makeSuite(BuildExtTestCase)
528
529if __name__ == '__main__':
530 support.run_unittest(test_suite())