You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.3 KiB
208 lines
7.3 KiB
import py
|
|
import sys, os, re
|
|
import shutil, subprocess, time
|
|
from testing.udir import udir
|
|
import cffi
|
|
|
|
|
|
local_dir = os.path.dirname(os.path.abspath(__file__))
|
|
_link_error = '?'
|
|
|
|
def check_lib_python_found(tmpdir):
|
|
global _link_error
|
|
if _link_error == '?':
|
|
ffi = cffi.FFI()
|
|
kwds = {}
|
|
ffi._apply_embedding_fix(kwds)
|
|
ffi.set_source("_test_lib_python_found", "", **kwds)
|
|
try:
|
|
ffi.compile(tmpdir=tmpdir, verbose=True)
|
|
except cffi.VerificationError as e:
|
|
_link_error = e
|
|
else:
|
|
_link_error = None
|
|
if _link_error:
|
|
py.test.skip(str(_link_error))
|
|
|
|
|
|
def prefix_pythonpath():
|
|
cffi_base = os.path.dirname(os.path.dirname(local_dir))
|
|
pythonpath = org_env.get('PYTHONPATH', '').split(os.pathsep)
|
|
if cffi_base not in pythonpath:
|
|
pythonpath.insert(0, cffi_base)
|
|
return os.pathsep.join(pythonpath)
|
|
|
|
def copy_away_env():
|
|
global org_env
|
|
try:
|
|
org_env
|
|
except NameError:
|
|
org_env = os.environ.copy()
|
|
|
|
|
|
class EmbeddingTests:
|
|
_compiled_modules = {}
|
|
|
|
def setup_method(self, meth):
|
|
check_lib_python_found(str(udir.ensure('embedding', dir=1)))
|
|
self._path = udir.join('embedding', meth.__name__)
|
|
if sys.platform == "win32" or sys.platform == "darwin":
|
|
self._compiled_modules.clear() # workaround
|
|
|
|
def get_path(self):
|
|
return str(self._path.ensure(dir=1))
|
|
|
|
def _run_base(self, args, **kwds):
|
|
print('RUNNING:', args, kwds)
|
|
return subprocess.Popen(args, **kwds)
|
|
|
|
def _run(self, args):
|
|
popen = self._run_base(args, cwd=self.get_path(),
|
|
stdout=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
output = popen.stdout.read()
|
|
err = popen.wait()
|
|
if err:
|
|
raise OSError("popen failed with exit code %r: %r" % (
|
|
err, args))
|
|
print(output.rstrip())
|
|
return output
|
|
|
|
def prepare_module(self, name):
|
|
self.patch_environment()
|
|
if name not in self._compiled_modules:
|
|
path = self.get_path()
|
|
filename = '%s.py' % name
|
|
# NOTE: if you have an .egg globally installed with an older
|
|
# version of cffi, this will not work, because sys.path ends
|
|
# up with the .egg before the PYTHONPATH entries. I didn't
|
|
# find a solution to that: we could hack sys.path inside the
|
|
# script run here, but we can't hack it in the same way in
|
|
# execute().
|
|
pathname = os.path.join(path, filename)
|
|
with open(pathname, 'w') as g:
|
|
g.write('''
|
|
# https://bugs.python.org/issue23246
|
|
import sys
|
|
if sys.platform == 'win32':
|
|
try:
|
|
import setuptools
|
|
except ImportError:
|
|
pass
|
|
''')
|
|
with open(os.path.join(local_dir, filename), 'r') as f:
|
|
g.write(f.read())
|
|
|
|
output = self._run([sys.executable, pathname])
|
|
match = re.compile(r"\bFILENAME: (.+)").search(output)
|
|
assert match
|
|
dynamic_lib_name = match.group(1)
|
|
if sys.platform == 'win32':
|
|
assert dynamic_lib_name.endswith('_cffi.dll')
|
|
elif sys.platform == 'darwin':
|
|
assert dynamic_lib_name.endswith('_cffi.dylib')
|
|
else:
|
|
assert dynamic_lib_name.endswith('_cffi.so')
|
|
self._compiled_modules[name] = dynamic_lib_name
|
|
return self._compiled_modules[name]
|
|
|
|
def compile(self, name, modules, opt=False, threads=False, defines={}):
|
|
path = self.get_path()
|
|
filename = '%s.c' % name
|
|
shutil.copy(os.path.join(local_dir, filename), path)
|
|
shutil.copy(os.path.join(local_dir, 'thread-test.h'), path)
|
|
import distutils.ccompiler
|
|
curdir = os.getcwd()
|
|
try:
|
|
os.chdir(self.get_path())
|
|
c = distutils.ccompiler.new_compiler()
|
|
print('compiling %s with %r' % (name, modules))
|
|
extra_preargs = []
|
|
debug = True
|
|
if sys.platform == 'win32':
|
|
libfiles = []
|
|
for m in modules:
|
|
m = os.path.basename(m)
|
|
assert m.endswith('.dll')
|
|
libfiles.append('Release\\%s.lib' % m[:-4])
|
|
modules = libfiles
|
|
extra_preargs.append('/MANIFEST')
|
|
debug = False # you need to install extra stuff
|
|
# for this to work
|
|
elif threads:
|
|
extra_preargs.append('-pthread')
|
|
objects = c.compile([filename], macros=sorted(defines.items()),
|
|
debug=debug)
|
|
c.link_executable(objects + modules, name, extra_preargs=extra_preargs)
|
|
finally:
|
|
os.chdir(curdir)
|
|
|
|
def patch_environment(self):
|
|
copy_away_env()
|
|
path = self.get_path()
|
|
# for libpypy-c.dll or Python27.dll
|
|
path = os.path.split(sys.executable)[0] + os.path.pathsep + path
|
|
env_extra = {'PYTHONPATH': prefix_pythonpath()}
|
|
if sys.platform == 'win32':
|
|
envname = 'PATH'
|
|
else:
|
|
envname = 'LD_LIBRARY_PATH'
|
|
libpath = org_env.get(envname)
|
|
if libpath:
|
|
libpath = path + os.path.pathsep + libpath
|
|
else:
|
|
libpath = path
|
|
env_extra[envname] = libpath
|
|
for key, value in sorted(env_extra.items()):
|
|
if os.environ.get(key) != value:
|
|
print('* setting env var %r to %r' % (key, value))
|
|
os.environ[key] = value
|
|
|
|
def execute(self, name):
|
|
path = self.get_path()
|
|
print('running %r in %r' % (name, path))
|
|
executable_name = name
|
|
if sys.platform == 'win32':
|
|
executable_name = os.path.join(path, executable_name + '.exe')
|
|
else:
|
|
executable_name = os.path.join('.', executable_name)
|
|
popen = self._run_base([executable_name], cwd=path,
|
|
stdout=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
result = popen.stdout.read()
|
|
err = popen.wait()
|
|
if err:
|
|
raise OSError("%r failed with exit code %r" % (name, err))
|
|
return result
|
|
|
|
|
|
class TestBasic(EmbeddingTests):
|
|
def test_empty(self):
|
|
empty_cffi = self.prepare_module('empty')
|
|
|
|
def test_basic(self):
|
|
add1_cffi = self.prepare_module('add1')
|
|
self.compile('add1-test', [add1_cffi])
|
|
output = self.execute('add1-test')
|
|
assert output == ("preparing...\n"
|
|
"adding 40 and 2\n"
|
|
"adding 100 and -5\n"
|
|
"got: 42 95\n")
|
|
|
|
def test_two_modules(self):
|
|
add1_cffi = self.prepare_module('add1')
|
|
add2_cffi = self.prepare_module('add2')
|
|
self.compile('add2-test', [add1_cffi, add2_cffi])
|
|
output = self.execute('add2-test')
|
|
assert output == ("preparing...\n"
|
|
"adding 40 and 2\n"
|
|
"prepADD2\n"
|
|
"adding 100 and -5 and -20\n"
|
|
"got: 42 75\n")
|
|
|
|
def test_init_time_error(self):
|
|
initerror_cffi = self.prepare_module('initerror')
|
|
self.compile('add1-test', [initerror_cffi])
|
|
output = self.execute('add1-test')
|
|
assert output == "got: 0 0\n" # plus lots of info to stderr
|