|
import os |
|
import sys |
|
|
|
try: |
|
basestring |
|
except NameError: |
|
|
|
basestring = str |
|
|
|
def error(msg): |
|
from cffi._shimmed_dist_utils import DistutilsSetupError |
|
raise DistutilsSetupError(msg) |
|
|
|
|
|
def execfile(filename, glob): |
|
|
|
|
|
|
|
|
|
|
|
with open(filename) as f: |
|
src = f.read() |
|
src += '\n' |
|
code = compile(src, filename, 'exec') |
|
exec(code, glob, glob) |
|
|
|
|
|
def add_cffi_module(dist, mod_spec): |
|
from cffi.api import FFI |
|
|
|
if not isinstance(mod_spec, basestring): |
|
error("argument to 'cffi_modules=...' must be a str or a list of str," |
|
" not %r" % (type(mod_spec).__name__,)) |
|
mod_spec = str(mod_spec) |
|
try: |
|
build_file_name, ffi_var_name = mod_spec.split(':') |
|
except ValueError: |
|
error("%r must be of the form 'path/build.py:ffi_variable'" % |
|
(mod_spec,)) |
|
if not os.path.exists(build_file_name): |
|
ext = '' |
|
rewritten = build_file_name.replace('.', '/') + '.py' |
|
if os.path.exists(rewritten): |
|
ext = ' (rewrite cffi_modules to [%r])' % ( |
|
rewritten + ':' + ffi_var_name,) |
|
error("%r does not name an existing file%s" % (build_file_name, ext)) |
|
|
|
mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} |
|
execfile(build_file_name, mod_vars) |
|
|
|
try: |
|
ffi = mod_vars[ffi_var_name] |
|
except KeyError: |
|
error("%r: object %r not found in module" % (mod_spec, |
|
ffi_var_name)) |
|
if not isinstance(ffi, FFI): |
|
ffi = ffi() |
|
if not isinstance(ffi, FFI): |
|
error("%r is not an FFI instance (got %r)" % (mod_spec, |
|
type(ffi).__name__)) |
|
if not hasattr(ffi, '_assigned_source'): |
|
error("%r: the set_source() method was not called" % (mod_spec,)) |
|
module_name, source, source_extension, kwds = ffi._assigned_source |
|
if ffi._windows_unicode: |
|
kwds = kwds.copy() |
|
ffi._apply_windows_unicode(kwds) |
|
|
|
if source is None: |
|
_add_py_module(dist, ffi, module_name) |
|
else: |
|
_add_c_module(dist, ffi, module_name, source, source_extension, kwds) |
|
|
|
def _set_py_limited_api(Extension, kwds): |
|
""" |
|
Add py_limited_api to kwds if setuptools >= 26 is in use. |
|
Do not alter the setting if it already exists. |
|
Setuptools takes care of ignoring the flag on Python 2 and PyPy. |
|
|
|
CPython itself should ignore the flag in a debugging version |
|
(by not listing .abi3.so in the extensions it supports), but |
|
it doesn't so far, creating troubles. That's why we check |
|
for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent |
|
of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) |
|
|
|
On Windows, with CPython <= 3.4, it's better not to use py_limited_api |
|
because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. |
|
Recently (2020) we started shipping only >= 3.5 wheels, though. So |
|
we'll give it another try and set py_limited_api on Windows >= 3.5. |
|
""" |
|
from cffi import recompiler |
|
|
|
if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') |
|
and recompiler.USE_LIMITED_API): |
|
import setuptools |
|
try: |
|
setuptools_major_version = int(setuptools.__version__.partition('.')[0]) |
|
if setuptools_major_version >= 26: |
|
kwds['py_limited_api'] = True |
|
except ValueError: |
|
|
|
|
|
|
|
kwds['py_limited_api'] = True |
|
return kwds |
|
|
|
def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): |
|
|
|
from setuptools.command.build_ext import build_ext |
|
from cffi._shimmed_dist_utils import Extension, log, mkpath |
|
from cffi import recompiler |
|
|
|
allsources = ['$PLACEHOLDER'] |
|
allsources.extend(kwds.pop('sources', [])) |
|
kwds = _set_py_limited_api(Extension, kwds) |
|
ext = Extension(name=module_name, sources=allsources, **kwds) |
|
|
|
def make_mod(tmpdir, pre_run=None): |
|
c_file = os.path.join(tmpdir, module_name + source_extension) |
|
log.info("generating cffi module %r" % c_file) |
|
mkpath(tmpdir) |
|
|
|
|
|
|
|
|
|
if pre_run is not None: |
|
pre_run(ext, ffi) |
|
updated = recompiler.make_c_source(ffi, module_name, source, c_file) |
|
if not updated: |
|
log.info("already up-to-date") |
|
return c_file |
|
|
|
if dist.ext_modules is None: |
|
dist.ext_modules = [] |
|
dist.ext_modules.append(ext) |
|
|
|
base_class = dist.cmdclass.get('build_ext', build_ext) |
|
class build_ext_make_mod(base_class): |
|
def run(self): |
|
if ext.sources[0] == '$PLACEHOLDER': |
|
pre_run = getattr(self, 'pre_run', None) |
|
ext.sources[0] = make_mod(self.build_temp, pre_run) |
|
base_class.run(self) |
|
dist.cmdclass['build_ext'] = build_ext_make_mod |
|
|
|
|
|
|
|
|
|
|
|
|
|
def _add_py_module(dist, ffi, module_name): |
|
from setuptools.command.build_py import build_py |
|
from setuptools.command.build_ext import build_ext |
|
from cffi._shimmed_dist_utils import log, mkpath |
|
from cffi import recompiler |
|
|
|
def generate_mod(py_file): |
|
log.info("generating cffi module %r" % py_file) |
|
mkpath(os.path.dirname(py_file)) |
|
updated = recompiler.make_py_source(ffi, module_name, py_file) |
|
if not updated: |
|
log.info("already up-to-date") |
|
|
|
base_class = dist.cmdclass.get('build_py', build_py) |
|
class build_py_make_mod(base_class): |
|
def run(self): |
|
base_class.run(self) |
|
module_path = module_name.split('.') |
|
module_path[-1] += '.py' |
|
generate_mod(os.path.join(self.build_lib, *module_path)) |
|
def get_source_files(self): |
|
|
|
|
|
saved_py_modules = self.py_modules |
|
try: |
|
if saved_py_modules: |
|
self.py_modules = [m for m in saved_py_modules |
|
if m != module_name] |
|
return base_class.get_source_files(self) |
|
finally: |
|
self.py_modules = saved_py_modules |
|
dist.cmdclass['build_py'] = build_py_make_mod |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if dist.py_modules is None: |
|
dist.py_modules = [] |
|
dist.py_modules.append(module_name) |
|
|
|
|
|
base_class_2 = dist.cmdclass.get('build_ext', build_ext) |
|
class build_ext_make_mod(base_class_2): |
|
def run(self): |
|
base_class_2.run(self) |
|
if self.inplace: |
|
|
|
module_path = module_name.split('.') |
|
package = '.'.join(module_path[:-1]) |
|
build_py = self.get_finalized_command('build_py') |
|
package_dir = build_py.get_package_dir(package) |
|
file_name = module_path[-1] + '.py' |
|
generate_mod(os.path.join(package_dir, file_name)) |
|
dist.cmdclass['build_ext'] = build_ext_make_mod |
|
|
|
def cffi_modules(dist, attr, value): |
|
assert attr == 'cffi_modules' |
|
if isinstance(value, basestring): |
|
value = [value] |
|
|
|
for cffi_module in value: |
|
add_cffi_module(dist, cffi_module) |
|
|