1import sys, os, platform 2import subprocess 3import errno 4 5# on Windows we give up and always import setuptools early to fix things for us 6if sys.platform == "win32": 7 import setuptools 8 9 10sources = ['c/_cffi_backend.c'] 11libraries = ['ffi'] 12include_dirs = ['/usr/include/ffi', 13 '/usr/include/libffi'] # may be changed by pkg-config 14define_macros = [] 15library_dirs = [] 16extra_compile_args = [] 17extra_link_args = [] 18 19 20def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False): 21 pkg_config = os.environ.get('PKG_CONFIG','pkg-config') 22 try: 23 p = subprocess.Popen([pkg_config, option, 'libffi'], 24 stdout=subprocess.PIPE) 25 except OSError as e: 26 if e.errno not in [errno.ENOENT, errno.EACCES]: 27 raise 28 else: 29 t = p.stdout.read().decode().strip() 30 p.stdout.close() 31 if p.wait() == 0: 32 res = t.split() 33 # '-I/usr/...' -> '/usr/...' 34 for x in res: 35 assert x.startswith(result_prefix) 36 res = [x[len(result_prefix):] for x in res] 37 #print 'PKG_CONFIG:', option, res 38 # 39 sysroot = sysroot and os.environ.get('PKG_CONFIG_SYSROOT_DIR', '') 40 if sysroot: 41 # old versions of pkg-config don't support this env var, 42 # so here we emulate its effect if needed 43 res = [path if path.startswith(sysroot) 44 else sysroot + path 45 for path in res] 46 # 47 resultlist[:] = res 48 49no_compiler_found = False 50def no_working_compiler_found(): 51 sys.stderr.write(""" 52 No working compiler found, or bogus compiler options passed to 53 the compiler from Python's standard "distutils" module. See 54 the error messages above. Likely, the problem is not related 55 to CFFI but generic to the setup.py of any Python package that 56 tries to compile C code. (Hints: on OS/X 10.8, for errors about 57 -mno-fused-madd see http://stackoverflow.com/questions/22313407/ 58 Otherwise, see https://wiki.python.org/moin/CompLangPython or 59 the IRC channel #python on irc.libera.chat.) 60 61 Trying to continue anyway. If you are trying to install CFFI from 62 a build done in a different context, you can ignore this warning. 63 \n""") 64 global no_compiler_found 65 no_compiler_found = True 66 67def get_config(): 68 from distutils.core import Distribution 69 from distutils.sysconfig import get_config_vars 70 get_config_vars() # workaround for a bug of distutils, e.g. on OS/X 71 config = Distribution().get_command_obj('config') 72 return config 73 74def ask_supports_thread(): 75 config = get_config() 76 ok = (sys.platform != 'win32' and 77 config.try_compile('__thread int some_threadlocal_variable_42;')) 78 if ok: 79 define_macros.append(('USE__THREAD', None)) 80 else: 81 ok1 = config.try_compile('int some_regular_variable_42;') 82 if not ok1: 83 no_working_compiler_found() 84 else: 85 sys.stderr.write("Note: will not use '__thread' in the C code\n") 86 _safe_to_ignore() 87 88def ask_supports_sync_synchronize(): 89 if sys.platform == 'win32' or no_compiler_found: 90 return 91 config = get_config() 92 ok = config.try_link('int main(void) { __sync_synchronize(); return 0; }') 93 if ok: 94 define_macros.append(('HAVE_SYNC_SYNCHRONIZE', None)) 95 else: 96 sys.stderr.write("Note: will not use '__sync_synchronize()'" 97 " in the C code\n") 98 _safe_to_ignore() 99 100def _safe_to_ignore(): 101 sys.stderr.write("***** The above error message can be safely ignored.\n\n") 102 103def uses_msvc(): 104 config = get_config() 105 return config.try_compile('#ifndef _MSC_VER\n#error "not MSVC"\n#endif') 106 107def use_pkg_config(): 108 if sys.platform == 'darwin' and os.path.exists('/usr/local/bin/brew'): 109 use_homebrew_for_libffi() 110 111 _ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True) 112 _ask_pkg_config(extra_compile_args, '--cflags-only-other') 113 _ask_pkg_config(library_dirs, '--libs-only-L', '-L', sysroot=True) 114 _ask_pkg_config(extra_link_args, '--libs-only-other') 115 _ask_pkg_config(libraries, '--libs-only-l', '-l') 116 117def use_homebrew_for_libffi(): 118 # We can build by setting: 119 # PKG_CONFIG_PATH = $(brew --prefix libffi)/lib/pkgconfig 120 with os.popen('brew --prefix libffi') as brew_prefix_cmd: 121 prefix = brew_prefix_cmd.read().strip() 122 pkgconfig = os.path.join(prefix, 'lib', 'pkgconfig') 123 os.environ['PKG_CONFIG_PATH'] = ( 124 os.environ.get('PKG_CONFIG_PATH', '') + ':' + pkgconfig) 125 126if sys.platform == "win32" and uses_msvc(): 127 if platform.machine() == "ARM64": 128 include_dirs.append(os.path.join("c/libffi_arm64/include")) 129 library_dirs.append(os.path.join("c/libffi_arm64")) 130 else: 131 COMPILE_LIBFFI = 'c/libffi_x86_x64' # from the CPython distribution 132 assert os.path.isdir(COMPILE_LIBFFI), "directory not found!" 133 include_dirs[:] = [COMPILE_LIBFFI] 134 libraries[:] = [] 135 _filenames = [filename.lower() for filename in os.listdir(COMPILE_LIBFFI)] 136 _filenames = [filename for filename in _filenames 137 if filename.endswith('.c')] 138 if sys.maxsize > 2**32: 139 # 64-bit: unlist win32.c, and add instead win64.obj. If the obj 140 # happens to get outdated at some point in the future, you need to 141 # rebuild it manually from win64.asm. 142 _filenames.remove('win32.c') 143 extra_link_args.append(os.path.join(COMPILE_LIBFFI, 'win64.obj')) 144 sources.extend(os.path.join(COMPILE_LIBFFI, filename) 145 for filename in _filenames) 146else: 147 use_pkg_config() 148 ask_supports_thread() 149 ask_supports_sync_synchronize() 150 151if 'darwin' in sys.platform: 152 # priority is given to `pkg_config`, but always fall back on SDK's libffi. 153 extra_compile_args += ['-iwithsysroot/usr/include/ffi'] 154 155if 'freebsd' in sys.platform: 156 include_dirs.append('/usr/local/include') 157 library_dirs.append('/usr/local/lib') 158 159if __name__ == '__main__': 160 from setuptools import setup, Distribution, Extension 161 162 class CFFIDistribution(Distribution): 163 def has_ext_modules(self): 164 # Event if we don't have extension modules (e.g. on PyPy) we want to 165 # claim that we do so that wheels get properly tagged as Python 166 # specific. (thanks dstufft!) 167 return True 168 169 # On PyPy, cffi is preinstalled and it is not possible, at least for now, 170 # to install a different version. We work around it by making the setup() 171 # arguments mostly empty in this case. 172 cpython = ('_cffi_backend' not in sys.builtin_module_names) 173 174 setup( 175 name='cffi', 176 description='Foreign Function Interface for Python calling C code.', 177 long_description=""" 178CFFI 179==== 180 181Foreign Function Interface for Python calling C code. 182Please see the `Documentation <http://cffi.readthedocs.org/>`_. 183 184Contact 185------- 186 187`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_ 188""", 189 version='1.15.0', 190 packages=['cffi'] if cpython else [], 191 package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h', 192 '_embedding.h', '_cffi_errors.h']} 193 if cpython else {}, 194 zip_safe=False, 195 196 url='http://cffi.readthedocs.org', 197 author='Armin Rigo, Maciej Fijalkowski', 198 author_email='python-cffi@googlegroups.com', 199 200 license='MIT', 201 202 distclass=CFFIDistribution, 203 ext_modules=[Extension( 204 name='_cffi_backend', 205 include_dirs=include_dirs, 206 sources=sources, 207 libraries=libraries, 208 define_macros=define_macros, 209 library_dirs=library_dirs, 210 extra_compile_args=extra_compile_args, 211 extra_link_args=extra_link_args, 212 )] if cpython else [], 213 214 install_requires=[ 215 'pycparser' if sys.version_info >= (2, 7) else 'pycparser<2.19', 216 ] if cpython else [], 217 218 entry_points = { 219 "distutils.setup_keywords": [ 220 "cffi_modules = cffi.setuptools_ext:cffi_modules", 221 ], 222 }, 223 224 classifiers=[ 225 'Programming Language :: Python', 226 'Programming Language :: Python :: 2', 227 'Programming Language :: Python :: 2.7', 228 'Programming Language :: Python :: 3', 229 'Programming Language :: Python :: 3.6', 230 'Programming Language :: Python :: 3.7', 231 'Programming Language :: Python :: 3.8', 232 'Programming Language :: Python :: 3.9', 233 'Programming Language :: Python :: 3.10', 234 'Programming Language :: Python :: Implementation :: CPython', 235 'Programming Language :: Python :: Implementation :: PyPy', 236 'License :: OSI Approved :: MIT License', 237 ], 238 ) 239