├── .github ├── actions │ └── dynamatrix │ │ ├── action.yml │ │ └── matrix_yaml_to_json.py └── workflows │ └── ci.yaml ├── .gitignore ├── .readthedocs.yaml ├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── README.md ├── demo ├── _curses.py ├── _curses_build.py ├── _curses_setup.py ├── api.py ├── bsdopendirtype.py ├── bsdopendirtype_build.py ├── bsdopendirtype_setup.py ├── btrfs-snap.py ├── cffi-cocoa.py ├── embedding.py ├── embedding_test.c ├── extern_python.py ├── extern_python_varargs.py ├── fastcsv.py ├── gmp.py ├── gmp_build.py ├── manual.c ├── manual2.py ├── pwuid.py ├── pwuid_build.py ├── py.cleanup ├── pyobj.py ├── readdir.py ├── readdir2.py ├── readdir2_build.py ├── readdir2_setup.py ├── readdir_build.py ├── readdir_ctypes.py ├── readdir_setup.py ├── recopendirtype.py ├── recopendirtype_build.py ├── setup_manual.py ├── winclipboard.py ├── winclipboard_build.py ├── xclient.py └── xclient_build.py ├── doc ├── Makefile ├── make.bat ├── misc │ ├── design.rst │ ├── grant-cffi-1.0.rst │ └── parse_c_type.rst └── source │ ├── cdef.rst │ ├── conf.py │ ├── embedding.rst │ ├── goals.rst │ ├── index.rst │ ├── installation.rst │ ├── overview.rst │ ├── ref.rst │ ├── using.rst │ └── whatsnew.rst ├── pyproject.toml ├── setup.py ├── setup_base.py ├── src ├── c │ ├── _cffi_backend.c │ ├── call_python.c │ ├── cdlopen.c │ ├── cffi1_module.c │ ├── cglob.c │ ├── commontypes.c │ ├── ffi_obj.c │ ├── file_emulator.h │ ├── lib_obj.c │ ├── libffi_arm64 │ │ ├── README │ │ ├── build_libffi.bat │ │ ├── ffi.lib │ │ └── include │ │ │ ├── ffi.h │ │ │ ├── fficonfig.h │ │ │ └── ffitarget.h │ ├── libffi_x86_x64 │ │ ├── LICENSE │ │ ├── README │ │ ├── README.ctypes │ │ ├── ffi.c │ │ ├── ffi.h │ │ ├── ffi_common.h │ │ ├── fficonfig.h │ │ ├── ffitarget.h │ │ ├── prep_cif.c │ │ ├── types.c │ │ ├── win32.c │ │ ├── win64.asm │ │ └── win64.obj │ ├── malloc_closure.h │ ├── minibuffer.h │ ├── misc_thread_common.h │ ├── misc_thread_posix.h │ ├── misc_win32.h │ ├── parse_c_type.c │ ├── realize_c_type.c │ ├── test_c.py │ ├── wchar_helper.h │ └── wchar_helper_3.h └── cffi │ ├── __init__.py │ ├── _cffi_errors.h │ ├── _cffi_include.h │ ├── _embedding.h │ ├── _imp_emulation.py │ ├── _shimmed_dist_utils.py │ ├── api.py │ ├── backend_ctypes.py │ ├── cffi_opcode.py │ ├── commontypes.py │ ├── cparser.py │ ├── error.py │ ├── ffiplatform.py │ ├── lock.py │ ├── model.py │ ├── parse_c_type.h │ ├── pkgconfig.py │ ├── recompiler.py │ ├── setuptools_ext.py │ ├── vengine_cpy.py │ ├── vengine_gen.py │ └── verifier.py ├── testing ├── __init__.py ├── cffi0 │ ├── __init__.py │ ├── backend_tests.py │ ├── callback_in_thread.py │ ├── snippets │ │ ├── distutils_module │ │ │ ├── setup.py │ │ │ └── snip_basic_verify.py │ │ ├── distutils_package_1 │ │ │ ├── setup.py │ │ │ └── snip_basic_verify1 │ │ │ │ └── __init__.py │ │ ├── distutils_package_2 │ │ │ ├── setup.py │ │ │ └── snip_basic_verify2 │ │ │ │ └── __init__.py │ │ ├── infrastructure │ │ │ ├── setup.py │ │ │ └── snip_infrastructure │ │ │ │ └── __init__.py │ │ ├── setuptools_module │ │ │ ├── setup.py │ │ │ └── snip_setuptools_verify.py │ │ ├── setuptools_package_1 │ │ │ ├── setup.py │ │ │ └── snip_setuptools_verify1 │ │ │ │ └── __init__.py │ │ └── setuptools_package_2 │ │ │ ├── setup.py │ │ │ └── snip_setuptools_verify2 │ │ │ └── __init__.py │ ├── test_cdata.py │ ├── test_ctypes.py │ ├── test_ffi_backend.py │ ├── test_function.py │ ├── test_model.py │ ├── test_ownlib.py │ ├── test_parsing.py │ ├── test_platform.py │ ├── test_unicode_literals.py │ ├── test_verify.py │ ├── test_verify2.py │ ├── test_version.py │ ├── test_vgen.py │ ├── test_vgen2.py │ ├── test_zdistutils.py │ └── test_zintegration.py ├── cffi1 │ ├── __init__.py │ ├── test_cffi_binary.py │ ├── test_commontypes.py │ ├── test_dlopen.py │ ├── test_dlopen_unicode_literals.py │ ├── test_ffi_obj.py │ ├── test_function_args.py │ ├── test_new_ffi_1.py │ ├── test_parse_c_type.py │ ├── test_pkgconfig.py │ ├── test_re_python.py │ ├── test_realize_c_type.py │ ├── test_recompiler.py │ ├── test_unicode_literals.py │ ├── test_verify1.py │ └── test_zdist.py ├── conftest.py ├── embedding │ ├── __init__.py │ ├── add1-test.c │ ├── add1.py │ ├── add2-test.c │ ├── add2.py │ ├── add3.py │ ├── add_recursive-test.c │ ├── add_recursive.py │ ├── empty-test.c │ ├── empty.py │ ├── initerror.py │ ├── perf-test.c │ ├── perf.py │ ├── test_basic.py │ ├── test_performance.py │ ├── test_recursive.py │ ├── test_thread.py │ ├── test_tlocal.py │ ├── thread-test.h │ ├── thread1-test.c │ ├── thread2-test.c │ ├── thread3-test.c │ ├── tlocal-test.c │ ├── tlocal.py │ └── withunicode.py ├── support.py └── udir.py └── tools └── version.py /.github/actions/dynamatrix/action.yml: -------------------------------------------------------------------------------- 1 | name: Create matrix 2 | description: Create matrix 3 | inputs: 4 | matrix_yaml: 5 | description: input yaml matrix as multiline string; any entry with a bool true `omit` key will be filtered from the output matrix 6 | required: true 7 | outputs: 8 | matrix_json: 9 | description: filtered matrix as JSON 10 | value: ${{ steps.matrix_gen.outputs.matrix_json }} 11 | 12 | runs: 13 | using: "composite" 14 | 15 | steps: 16 | - id: matrix_gen 17 | run: | 18 | # FIXME: input sanity check to prevent shell injection 19 | python3 $GITHUB_ACTION_PATH/matrix_yaml_to_json.py --from-stdin << EOF 20 | ${{ inputs.matrix_yaml }} 21 | EOF 22 | shell: bash 23 | -------------------------------------------------------------------------------- /.github/actions/dynamatrix/matrix_yaml_to_json.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | import json 5 | import os 6 | import pathlib 7 | import sys 8 | import typing as t 9 | import yaml 10 | 11 | from collections.abc import MutableMapping, Sequence 12 | 13 | skipped_entries = [] 14 | 15 | def _filter_omit_entries(value): 16 | if isinstance(value, MutableMapping): 17 | if (omit_value := value.pop('omit', ...)) is not ...: 18 | if omit_value is True or str(omit_value).lower().strip() == 'true': 19 | print(f'omitting {value} from matrix') 20 | skipped_entries.append(value) 21 | return ... 22 | 23 | return {k: v for k, v in ((k, _filter_omit_entries(v)) for k, v in value.items()) if v is not ...} 24 | 25 | if isinstance(value, str): 26 | return value 27 | 28 | if isinstance(value, Sequence): 29 | return [v for v in (_filter_omit_entries(v) for v in value) if v is not ...] 30 | 31 | return value 32 | 33 | def main(): 34 | p = argparse.ArgumentParser(description='GHA YAML matrix filter') 35 | required_grp = p.add_mutually_exclusive_group(required=True) 36 | required_grp.add_argument('--from-stdin', action='store_true', help='read input YAML from stdin') 37 | required_grp.add_argument('--from-file', type=pathlib.Path, help='read input YAML from file path') 38 | 39 | args = p.parse_args() 40 | 41 | path: pathlib.Path | None 42 | 43 | matrix_yaml: str 44 | 45 | if path := args.from_file: 46 | matrix_yaml = path.read_text() 47 | elif args.from_stdin: 48 | matrix_yaml = sys.stdin.read() 49 | else: 50 | raise Exception('no source provided for matrix yaml') 51 | 52 | raw_matrix = yaml.safe_load(matrix_yaml) 53 | filtered_matrix = _filter_omit_entries(raw_matrix) 54 | 55 | output_matrix_json = json.dumps(filtered_matrix) 56 | output_skipped_matrix_json = json.dumps(skipped_entries) 57 | 58 | print(f'filtered matrix: {output_matrix_json}') 59 | print(f'skipped entries: {output_skipped_matrix_json}') 60 | 61 | if (gh_output := os.environ.get('GITHUB_OUTPUT')): 62 | print('setting step output var matrix_json; skipped_matrix_json...') 63 | with pathlib.Path(gh_output).open('a') as env_fd: 64 | env_fd.write(f'matrix_json<<__MATRIX_EOF\n{output_matrix_json}\n__MATRIX_EOF\n') 65 | env_fd.write(f'skipped_matrix_json<<__MATRIX_EOF\n{output_skipped_matrix_json}\n__MATRIX_EOF\n') 66 | else: 67 | print("GITHUB_OUTPUT not set; skipping variable output") 68 | 69 | 70 | if __name__ == '__main__': 71 | main() 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | /dist/ 3 | *.py[cod] 4 | __pycache__/ 5 | *.egg-info/ 6 | *.so 7 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: doc/source/conf.py 17 | 18 | # We recommend specifying your dependencies to enable reproducible builds: 19 | # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html 20 | # python: 21 | # install: 22 | # - requirements: docs/requirements.txt 23 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This package has been mostly done by Armin Rigo with help from 2 | Maciej Fijałkowski. The idea is heavily based (although not directly 3 | copied) from LuaJIT ffi by Mike Pall. 4 | 5 | 6 | Other contributors: 7 | 8 | Google Inc. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Except when otherwise stated (look for LICENSE files in directories or 3 | information at the beginning of each file) all software and 4 | documentation is licensed as follows: 5 | 6 | The MIT License 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation 10 | files (the "Software"), to deal in the Software without 11 | restriction, including without limitation the rights to use, 12 | copy, modify, merge, publish, distribute, sublicense, and/or 13 | sell copies of the Software, and to permit persons to whom the 14 | Software is furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | 27 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include src/cffi *.py *.h 2 | recursive-include src/c *.c *.h *.asm *.py win64.obj ffi.lib 3 | recursive-include testing *.py *.c *.h 4 | recursive-include doc *.py *.rst Makefile *.bat 5 | recursive-include demo py.cleanup *.py embedding_test.c manual.c 6 | include AUTHORS LICENSE setup.py setup_base.py 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub Actions Status](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/python-cffi/cffi/actions/workflows/ci.yaml?query=branch%3Amain++) 2 | [![PyPI version](https://img.shields.io/pypi/v/cffi.svg)](https://pypi.org/project/cffi) 3 | [![Read the Docs](https://img.shields.io/badge/docs-latest-blue.svg)][Documentation] 4 | 5 | 6 | CFFI 7 | ==== 8 | 9 | Foreign Function Interface for Python calling C code. 10 | 11 | Please see the [Documentation] or uncompiled in the `doc/` subdirectory. 12 | 13 | Download 14 | -------- 15 | 16 | [Download page](https://github.com/python-cffi/cffi/releases) 17 | 18 | Source Code 19 | ----------- 20 | 21 | Source code is publicly available on 22 | [GitHub](https://github.com/python-cffi/cffi). 23 | 24 | Contact 25 | ------- 26 | 27 | [Mailing list](https://groups.google.com/forum/#!forum/python-cffi) 28 | 29 | Testing/development tips 30 | ------------------------ 31 | 32 | After `git clone` or `wget && tar`, we will get a directory called `cffi` or `cffi-x.x.x`. we call it `repo-directory`. To run tests under CPython, run the following in the `repo-directory`: 33 | 34 | pip install pytest 35 | pip install -e . # editable install of CFFI for local development 36 | pytest src/c/ testing/ 37 | 38 | [Documentation]: http://cffi.readthedocs.org/ 39 | -------------------------------------------------------------------------------- /demo/_curses_setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="_curses", 5 | version="0.1", 6 | py_modules=["_curses"], 7 | setup_requires=["cffi>=1.0.dev0"], 8 | cffi_modules=[ 9 | "_curses_build.py:ffi", 10 | ], 11 | install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? 12 | zip_safe=False, 13 | ) 14 | -------------------------------------------------------------------------------- /demo/api.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | from cffi import FFI 3 | 4 | class PythonFFI(FFI): 5 | 6 | def __init__(self, backend=None): 7 | FFI.__init__(self, backend=backend) 8 | self._pyexports = {} 9 | 10 | def pyexport(self, signature): 11 | tp = self._typeof(signature, consider_function_as_funcptr=True) 12 | def decorator(func): 13 | name = func.__name__ 14 | if name in self._pyexports: 15 | raise cffi.CDefError("duplicate pyexport'ed function %r" 16 | % (name,)) 17 | callback_var = self.getctype(tp, name) 18 | self.cdef("%s;" % callback_var) 19 | self._pyexports[name] = _PyExport(tp, func) 20 | return decorator 21 | 22 | def verify(self, source='', **kwargs): 23 | extras = [] 24 | pyexports = sorted(self._pyexports.items()) 25 | for name, export in pyexports: 26 | callback_var = self.getctype(export.tp, name) 27 | extras.append("%s;" % callback_var) 28 | extras.append(source) 29 | source = '\n'.join(extras) 30 | lib = FFI.verify(self, source, **kwargs) 31 | for name, export in pyexports: 32 | cb = self.callback(export.tp, export.func) 33 | export.cb = cb 34 | setattr(lib, name, cb) 35 | return lib 36 | 37 | 38 | class _PyExport(object): 39 | def __init__(self, tp, func): 40 | self.tp = tp 41 | self.func = func 42 | 43 | 44 | if __name__ == '__main__': 45 | ffi = PythonFFI() 46 | 47 | @ffi.pyexport("int(int)") 48 | def add1(n): 49 | print(n) 50 | return n + 1 51 | 52 | ffi.cdef(""" 53 | int f(int); 54 | """) 55 | 56 | lib = ffi.verify(""" 57 | int f(int x) { 58 | return add1(add1(x)); 59 | } 60 | """) 61 | 62 | assert lib.f(5) == 7 63 | -------------------------------------------------------------------------------- /demo/bsdopendirtype.py: -------------------------------------------------------------------------------- 1 | import os 2 | from _bsdopendirtype import ffi, lib 3 | 4 | 5 | def _posix_error(): 6 | raise OSError(ffi.errno, os.strerror(ffi.errno)) 7 | 8 | _dtype_to_smode = { 9 | lib.DT_BLK: 0o060000, 10 | lib.DT_CHR: 0o020000, 11 | lib.DT_DIR: 0o040000, 12 | lib.DT_FIFO: 0o010000, 13 | lib.DT_LNK: 0o120000, 14 | lib.DT_REG: 0o100000, 15 | lib.DT_SOCK: 0o140000, 16 | } 17 | 18 | def opendir(dir): 19 | if len(dir) == 0: 20 | dir = b'.' 21 | dirname = dir 22 | if not dirname.endswith(b'/'): 23 | dirname += b'/' 24 | dirp = lib.opendir(dir) 25 | if dirp == ffi.NULL: 26 | raise _posix_error() 27 | try: 28 | while True: 29 | ffi.errno = 0 30 | dirent = lib.readdir(dirp) 31 | if dirent == ffi.NULL: 32 | if ffi.errno != 0: 33 | raise _posix_error() 34 | return 35 | name = ffi.string(dirent.d_name) 36 | if name == b'.' or name == b'..': 37 | continue 38 | name = dirname + name 39 | try: 40 | smode = _dtype_to_smode[dirent.d_type] 41 | except KeyError: 42 | smode = os.lstat(name).st_mode 43 | yield name, smode 44 | finally: 45 | lib.closedir(dirp) 46 | 47 | if __name__ == '__main__': 48 | for name, smode in opendir(b'/tmp'): 49 | print(hex(smode), name) 50 | -------------------------------------------------------------------------------- /demo/bsdopendirtype_build.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | 3 | ffibuilder = FFI() 4 | ffibuilder.cdef(""" 5 | typedef ... DIR; 6 | struct dirent { 7 | unsigned char d_type; /* type of file */ 8 | char d_name[]; /* filename */ 9 | ...; 10 | }; 11 | DIR *opendir(const char *name); 12 | int closedir(DIR *dirp); 13 | struct dirent *readdir(DIR *dirp); 14 | static const int DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK; 15 | """) 16 | 17 | ffibuilder.set_source("_bsdopendirtype", """ 18 | #include 19 | #include 20 | """) 21 | 22 | if __name__ == '__main__': 23 | ffibuilder.compile(verbose=True) 24 | -------------------------------------------------------------------------------- /demo/bsdopendirtype_setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="example", 5 | version="0.1", 6 | py_modules=["bsdopendirtype"], 7 | setup_requires=["cffi>=1.0.dev0"], 8 | cffi_modules=[ 9 | "bsdopendirtype_build.py:ffibuilder", 10 | ], 11 | install_requires=["cffi>=1.0.dev0"], # should maybe be "cffi-backend" only? 12 | zip_safe=False, 13 | ) 14 | -------------------------------------------------------------------------------- /demo/btrfs-snap.py: -------------------------------------------------------------------------------- 1 | """ 2 | btrfs-snap.py: source target newname 3 | 4 | creates a exactly named snapshots and bails out if they exist 5 | """ 6 | from __future__ import print_function 7 | 8 | import argparse 9 | import fcntl 10 | import os 11 | import sys 12 | 13 | import cffi 14 | 15 | ffi = cffi.FFI() 16 | 17 | ffi.cdef(""" 18 | #define BTRFS_IOC_SNAP_CREATE_V2 ... 19 | struct btrfs_ioctl_vol_args_v2 { 20 | int64_t fd; 21 | char name[]; 22 | ...; 23 | }; 24 | """) 25 | 26 | ffi.set_source("_btrfs_cffi", "#include ") 27 | ffi.compile() 28 | 29 | # ____________________________________________________________ 30 | 31 | 32 | from _btrfs_cffi import ffi, lib 33 | 34 | parser = argparse.ArgumentParser(usage=__doc__.strip()) 35 | parser.add_argument('source', help='source subvolume') 36 | parser.add_argument('target', help='target directory') 37 | parser.add_argument('newname', help='name of the new snapshot') 38 | opts = parser.parse_args() 39 | 40 | source = os.open(opts.source, os.O_DIRECTORY) 41 | target = os.open(opts.target, os.O_DIRECTORY) 42 | 43 | 44 | args = ffi.new('struct btrfs_ioctl_vol_args_v2 *') 45 | args.name = opts.newname 46 | args.fd = source 47 | args_buffer = ffi.buffer(args) 48 | try: 49 | fcntl.ioctl(target, lib.BTRFS_IOC_SNAP_CREATE_V2, args_buffer) 50 | except IOError as e: 51 | print(e) 52 | sys.exit(1) 53 | 54 | -------------------------------------------------------------------------------- /demo/cffi-cocoa.py: -------------------------------------------------------------------------------- 1 | # Based on http://cocoawithlove.com/2010/09/minimalist-cocoa-programming.html 2 | # by Juraj Sukop. This demo was eventually expanded into a more complete 3 | # Cocoa library available at https://bitbucket.org/sukop/nspython . 4 | 5 | from cffi import FFI 6 | 7 | ffi = FFI() 8 | ffi.cdef(''' 9 | 10 | typedef signed char BOOL; 11 | 12 | typedef long NSInteger; 13 | typedef unsigned long NSUInteger; 14 | typedef NSInteger NSApplicationActivationPolicy; 15 | typedef NSUInteger NSBackingStoreType; 16 | typedef NSUInteger NSStringEncoding; 17 | 18 | typedef double CGFloat; 19 | struct CGPoint { 20 | CGFloat x; 21 | CGFloat y; 22 | }; 23 | typedef struct CGPoint CGPoint; 24 | struct CGSize { 25 | CGFloat width; 26 | CGFloat height; 27 | }; 28 | typedef struct CGSize CGSize; 29 | struct CGRect { 30 | CGPoint origin; 31 | CGSize size; 32 | }; 33 | typedef struct CGRect CGRect; 34 | 35 | typedef CGPoint NSPoint; 36 | typedef CGSize NSSize; 37 | typedef CGRect NSRect; 38 | 39 | typedef struct objc_class *Class; 40 | typedef struct objc_object { 41 | Class isa; 42 | } *id; 43 | typedef struct objc_selector *SEL; 44 | 45 | SEL sel_registerName(const char *str); 46 | id objc_getClass(const char *name); 47 | id objc_msgSend(id theReceiver, SEL theSelector, ...); 48 | 49 | ''') 50 | 51 | objc = ffi.dlopen('objc') 52 | appkit = ffi.dlopen('AppKit') 53 | 54 | nil = ffi.NULL 55 | YES = ffi.cast('BOOL', 1) 56 | NO = ffi.cast('BOOL', 0) 57 | 58 | NSASCIIStringEncoding = ffi.cast('NSStringEncoding', 1) 59 | NSApplicationActivationPolicyRegular = ffi.cast('NSApplicationActivationPolicy', 0) 60 | NSTitledWindowMask = ffi.cast('NSUInteger', 1) 61 | NSBackingStoreBuffered = ffi.cast('NSBackingStoreType', 2) 62 | 63 | NSMakePoint = lambda x, y: ffi.new('NSPoint *', (x, y))[0] 64 | NSMakeRect = lambda x, y, w, h: ffi.new('NSRect *', ((x, y), (w, h)))[0] 65 | 66 | get, send, sel = objc.objc_getClass, objc.objc_msgSend, objc.sel_registerName 67 | at = lambda s: send( 68 | get('NSString'), 69 | sel('stringWithCString:encoding:'), 70 | ffi.new('char[]', s), NSASCIIStringEncoding) 71 | 72 | send(get('NSAutoreleasePool'), sel('new')) 73 | app = send(get('NSApplication'), sel('sharedApplication')) 74 | send(app, sel('setActivationPolicy:'), NSApplicationActivationPolicyRegular) 75 | 76 | menubar = send(send(get('NSMenu'), sel('new')), sel('autorelease')) 77 | appMenuItem = send(send(get('NSMenuItem'), sel('new')), sel('autorelease')) 78 | send(menubar, sel('addItem:'), appMenuItem) 79 | send(app, sel('setMainMenu:'), menubar) 80 | 81 | appMenu = send(send(get('NSMenu'), sel('new')), sel('autorelease')) 82 | appName = send(send(get('NSProcessInfo'), sel('processInfo')), sel('processName')) 83 | quitTitle = send(at('Quit '), sel('stringByAppendingString:'), appName) 84 | quitMenuItem = send(send(send( 85 | get('NSMenuItem'), sel('alloc')), 86 | sel('initWithTitle:action:keyEquivalent:'), 87 | quitTitle, sel('terminate:'), at('q')), 88 | sel('autorelease')) 89 | send(appMenu, sel('addItem:'), quitMenuItem) 90 | send(appMenuItem, sel('setSubmenu:'), appMenu) 91 | 92 | window = send(send(send( 93 | get('NSWindow'), sel('alloc')), 94 | sel('initWithContentRect:styleMask:backing:defer:'), 95 | NSMakeRect(0, 0, 200, 200), NSTitledWindowMask, NSBackingStoreBuffered, NO), 96 | sel('autorelease')) 97 | send(window, sel('cascadeTopLeftFromPoint:'), NSMakePoint(20, 20)) 98 | send(window, sel('setTitle:'), appName) 99 | send(window, sel('makeKeyAndOrderFront:'), nil) 100 | 101 | send(app, sel('activateIgnoringOtherApps:'), YES) 102 | send(app, sel('run')) 103 | -------------------------------------------------------------------------------- /demo/embedding.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffibuilder = cffi.FFI() 4 | 5 | ffibuilder.embedding_api(""" 6 | int add(int, int); 7 | """) 8 | 9 | ffibuilder.embedding_init_code(""" 10 | from _embedding_cffi import ffi 11 | print("preparing") # printed once 12 | 13 | @ffi.def_extern() 14 | def add(x, y): 15 | print("adding %d and %d" % (x, y)) 16 | return x + y 17 | """) 18 | 19 | ffibuilder.set_source("_embedding_cffi", "") 20 | 21 | ffibuilder.compile(verbose=True) 22 | -------------------------------------------------------------------------------- /demo/embedding_test.c: -------------------------------------------------------------------------------- 1 | /* There are two options: 2 | 3 | =====1===== 4 | 5 | Link this program with _embedding_test.so. 6 | E.g. with gcc: 7 | 8 | gcc -o embedding_test embedding_test.c _embedding_cffi*.so 9 | 10 | You must then run the executable with the right command 11 | (LD_LIBRARY_PATH on Linux), otherwise it won't find the 12 | _embedding_cffi*.so: 13 | 14 | LD_LIBRARY_PATH=. ./embedding_test 15 | 16 | There are platform-specific options to gcc to avoid needing 17 | that, too. Linux: 18 | 19 | gcc -o embedding_test embedding_test.c _embedding_cffi*.so \ 20 | -Wl,-rpath=\$ORIGIN/ 21 | 22 | =====2===== 23 | 24 | Compile and link the _embedding_test.c source code together with 25 | this example (e.g. with PyPy): 26 | 27 | gcc -o embedding_test embedding_test.c _embedding_cffi.c \ 28 | -I/opt/pypy/include -pthread -lpypy-c 29 | */ 30 | 31 | #include 32 | 33 | extern int add(int x, int y); 34 | 35 | 36 | int main(void) 37 | { 38 | int res = add(40, 2); 39 | printf("result: %d\n", res); 40 | res = add(100, -5); 41 | printf("result: %d\n", res); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /demo/extern_python.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.cdef("""int my_algo(int); extern "Python" int f(int);""") 6 | 7 | ffi.set_source("_extern_python_cffi", """ 8 | static int f(int); 9 | static int my_algo(int n) { 10 | int i, sum = 0; 11 | for (i = 0; i < n; i++) 12 | sum += f(i); 13 | return sum; 14 | } 15 | """) 16 | 17 | ffi.compile() 18 | 19 | 20 | from _extern_python_cffi import ffi, lib 21 | 22 | @ffi.def_extern() 23 | def f(n): 24 | return n * n 25 | 26 | assert lib.my_algo(10) == 0+1+4+9+16+25+36+49+64+81 27 | -------------------------------------------------------------------------------- /demo/extern_python_varargs.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import cffi 3 | 4 | ffi = cffi.FFI() 5 | 6 | ffi.cdef(""" 7 | int my_algo(int); 8 | typedef ... va_list; 9 | extern "Python" int f(int, va_list *); 10 | 11 | int fetch_int(va_list *); 12 | double fetch_double(va_list *); 13 | void *fetch_ptr(va_list *); 14 | """) 15 | 16 | ffi.set_source("_extern_python_cffi", """ 17 | #include 18 | 19 | static int f(int, va_list *); 20 | 21 | static int f1(int n, ...) 22 | { 23 | va_list ap; 24 | va_start(ap, n); 25 | int res = f(n, &ap); 26 | va_end(ap); 27 | return res; 28 | } 29 | 30 | static int fetch_int(va_list *va) { return va_arg((*va), int); } 31 | static double fetch_double(va_list *va) { return va_arg((*va), double); } 32 | static void * fetch_ptr(va_list *va) { return va_arg((*va), void *); } 33 | 34 | static int my_algo(int n) { 35 | return f1(3, n, n+1, n+2) + f1(1, &n) + f1(2, 12.3, 45.6); 36 | } 37 | """) 38 | 39 | ffi.compile() 40 | 41 | 42 | from _extern_python_cffi import ffi, lib 43 | 44 | @ffi.def_extern() 45 | def f(n, va): 46 | if n == 3: 47 | x = lib.fetch_int(va) 48 | y = lib.fetch_int(va) 49 | z = lib.fetch_int(va) 50 | print(x, y, z) 51 | elif n == 1: 52 | ptr = lib.fetch_ptr(va) 53 | print('ptr to:', ffi.cast("int *", ptr)[0]) 54 | elif n == 2: 55 | x = lib.fetch_double(va) 56 | y = lib.fetch_double(va) 57 | print(x, y) 58 | else: 59 | raise AssertionError(n) 60 | return 14 61 | 62 | print(lib.my_algo(10)) 63 | -------------------------------------------------------------------------------- /demo/gmp.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | # 4 | # This is only a demo based on the GMP library. 5 | # There is a rather more complete (but perhaps outdated) version available at: 6 | # http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files 7 | # 8 | 9 | try: 10 | from _gmp_cffi import ffi, lib 11 | except ImportError: 12 | print('run gmp_build first, then make sure the shared object is on sys.path') 13 | sys.exit(1) 14 | 15 | # ffi "knows" about the declared variables and functions from the 16 | # cdef parts of the module created from gmp_build 17 | # lib "knows" how to call the functions from the set_source parts 18 | # of the module. 19 | 20 | # ____________________________________________________________ 21 | 22 | a = ffi.new("mpz_t") 23 | b = ffi.new("mpz_t") 24 | 25 | if len(sys.argv) < 3: 26 | print('call as %s bigint1, bigint2' % sys.argv[0]) 27 | sys.exit(2) 28 | 29 | lib.mpz_init_set_str(a, sys.argv[1], 10) # Assume decimal integers 30 | lib.mpz_init_set_str(b, sys.argv[2], 10) # Assume decimal integers 31 | lib.mpz_add(a, a, b) # a=a+b 32 | 33 | s = lib.mpz_get_str(ffi.NULL, 10, a) 34 | print(ffi.string(s)) 35 | -------------------------------------------------------------------------------- /demo/gmp_build.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | # 4 | # This is only a demo based on the GMP library. 5 | # There is a rather more complete (but perhaps outdated) version available at: 6 | # http://bazaar.launchpad.net/~tolot-solar-empire/+junk/gmpy_cffi/files 7 | # 8 | 9 | ffibuilder = cffi.FFI() 10 | 11 | ffibuilder.cdef(""" 12 | 13 | typedef struct { ...; } MP_INT; 14 | typedef MP_INT mpz_t[1]; 15 | 16 | int mpz_init_set_str (MP_INT *dest_integer, char *src_cstring, int base); 17 | void mpz_add (MP_INT *sum, MP_INT *addend1, MP_INT *addend2); 18 | char * mpz_get_str (char *string, int base, MP_INT *integer); 19 | 20 | """) 21 | 22 | ffibuilder.set_source('_gmp_cffi', "#include ", 23 | libraries=['gmp', 'm']) 24 | 25 | if __name__ == '__main__': 26 | ffibuilder.compile(verbose=True) 27 | -------------------------------------------------------------------------------- /demo/manual.c: -------------------------------------------------------------------------------- 1 | #include "_cffi_include.h" 2 | 3 | 4 | #define AA (42) 5 | #define BB (&bb) 6 | static int bb = 16261; 7 | 8 | int foo42(int a, int *b) 9 | { 10 | return a - *b; 11 | } 12 | 13 | int foo64(int a) 14 | { 15 | return ~a; 16 | } 17 | 18 | struct foo_s { 19 | int a; 20 | }; 21 | 22 | /************************************************************/ 23 | 24 | static void *_cffi_types[] = { 25 | _CFFI_OP(_CFFI_OP_FUNCTION, 1), 26 | _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), 27 | _CFFI_OP(_CFFI_OP_POINTER, 1), 28 | _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), 29 | _CFFI_OP(_CFFI_OP_FUNCTION, 1), 30 | _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT), 31 | _CFFI_OP(_CFFI_OP_FUNCTION_END, 0), 32 | _CFFI_OP(_CFFI_OP_STRUCT_UNION, 0), 33 | }; 34 | 35 | #ifndef PYPY_VERSION 36 | static PyObject * 37 | _cffi_f_foo42(PyObject *self, PyObject *args) 38 | { 39 | int x0; 40 | int * x1; 41 | Py_ssize_t datasize; 42 | int result; 43 | PyObject *arg0; 44 | PyObject *arg1; 45 | 46 | if (!PyArg_ParseTuple(args, "OO:foo42", &arg0, &arg1)) 47 | return NULL; 48 | 49 | x0 = _cffi_to_c_int(arg0, int); 50 | if (x0 == (int)-1 && PyErr_Occurred()) 51 | return NULL; 52 | 53 | datasize = _cffi_prepare_pointer_call_argument( 54 | _cffi_types[1], arg1, (char **)&x1); 55 | if (datasize != 0) { 56 | if (datasize < 0) 57 | return NULL; 58 | x1 = alloca(datasize); 59 | memset((void *)x1, 0, datasize); 60 | if (_cffi_convert_array_from_object((char *)x1, _cffi_types[1], arg1) < 0) 61 | return NULL; 62 | } 63 | 64 | Py_BEGIN_ALLOW_THREADS 65 | _cffi_restore_errno(); 66 | { result = foo42(x0, x1); } 67 | _cffi_save_errno(); 68 | Py_END_ALLOW_THREADS 69 | 70 | return _cffi_from_c_int(result, int); 71 | } 72 | #else 73 | static int _cffi_f_foo42(int x0, int *x1) 74 | { 75 | return foo42(x0, x1); 76 | } 77 | #endif 78 | 79 | #ifndef PYPY_VERSION 80 | static PyObject * 81 | _cffi_f_foo64(PyObject *self, PyObject *arg0) 82 | { 83 | int x0; 84 | int result; 85 | 86 | x0 = _cffi_to_c_int(arg0, int); 87 | if (x0 == (int)-1 && PyErr_Occurred()) 88 | return NULL; 89 | 90 | Py_BEGIN_ALLOW_THREADS 91 | _cffi_restore_errno(); 92 | { result = foo64(x0); } 93 | _cffi_save_errno(); 94 | Py_END_ALLOW_THREADS 95 | 96 | return _cffi_from_c_int(result, int); 97 | } 98 | #else 99 | static int _cffi_f_foo64(int x0) 100 | { 101 | return foo64(x0); 102 | } 103 | #endif 104 | 105 | static int _cffi_const_AA(unsigned long long *output) 106 | { 107 | *output = (unsigned long long)((AA) << 0); // integer 108 | return (AA) <= 0; 109 | } 110 | 111 | static void _cffi_const_BB(char *output) 112 | { 113 | *(int **)output = BB; 114 | } 115 | 116 | static const struct _cffi_global_s _cffi_globals[] = { 117 | { "AA", &_cffi_const_AA, _CFFI_OP(_CFFI_OP_CONSTANT_INT, 0) }, 118 | { "BB", &_cffi_const_BB, _CFFI_OP(_CFFI_OP_CONSTANT, 2) }, 119 | { "bb", &bb, _CFFI_OP(_CFFI_OP_GLOBAL_VAR, 1) }, 120 | { "foo42", &_cffi_f_foo42, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_V, 0) }, 121 | { "foo64", &_cffi_f_foo64, _CFFI_OP(_CFFI_OP_CPYTHON_BLTN_O, 4) }, 122 | }; 123 | 124 | struct _cffi_align_foo_s { char x; struct foo_s y; }; 125 | 126 | static const struct _cffi_struct_union_s _cffi_struct_unions[] = { 127 | { "foo_s", 7, 0, 128 | sizeof(struct foo_s), 129 | offsetof(struct _cffi_align_foo_s, y), 130 | 1, 0 }, 131 | }; 132 | 133 | static const struct _cffi_field_s _cffi_fields[] = { 134 | { "a", offsetof(struct foo_s, a), sizeof(((struct foo_s *)0)->a), 135 | _CFFI_OP(_CFFI_OP_NOOP, 1) }, 136 | }; 137 | 138 | static const struct _cffi_type_context_s _cffi_type_context = { 139 | _cffi_types, 140 | _cffi_globals, 141 | _cffi_fields, 142 | _cffi_struct_unions, 143 | NULL, 144 | NULL, 145 | 5, /* num_globals */ 146 | 1, /* num_struct_unions */ 147 | 0, 148 | 0, 149 | NULL, 150 | 8, /* num_types */ 151 | }; 152 | 153 | #ifndef PYPY_VERSION 154 | PyMODINIT_FUNC 155 | initmanual(void) 156 | { 157 | _cffi_init("manual", 0x2601, &_cffi_type_context); 158 | } 159 | #else 160 | PyMODINIT_FUNC 161 | _cffi_pypyinit_manual(const void *p[]) 162 | { 163 | p[0] = (const void *)0x2601; 164 | p[1] = &_cffi_type_context; 165 | } 166 | #endif 167 | -------------------------------------------------------------------------------- /demo/manual2.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import _cffi_backend 3 | 4 | ffi = _cffi_backend.FFI(b"manual2", 5 | _version = 0x2601, 6 | _types = b'\x00\x00\x01\x0D\x00\x00\x07\x01\x00\x00\x00\x0F\x00\x00\x00\x09\x00\x00\x00\x0B\x00\x00\x01\x03', 7 | _globals = (b'\xff\xff\xff\x0bAA',0,b'\xff\xff\xff\x0bBB',-1,b'\xff\xff\xff\x0bCC',2,b'\xff\xff\xff\x1fFOO',0x9999999999999999,b'\x00\x00\x00#close',0,b'\x00\x00\x05#stdout',0), 8 | _struct_unions = ((b'\x00\x00\x00\x03\x00\x00\x00\x00point_s',b'\x00\x00\x01\x11\xff\xff\xff\xffx',b'\x00\x00\x01\x11\xff\xff\xff\xffy'),), 9 | _enums = (b'\x00\x00\x00\x04\x00\x00\x00\x07myenum_e\x00AA,BB,CC',), 10 | _typenames = (b'\x00\x00\x00\x01myint_t',), 11 | ) 12 | 13 | 14 | 15 | # trying it out 16 | lib = ffi.dlopen(None) 17 | assert lib.AA == 0 18 | assert lib.BB == -1 19 | assert lib.FOO == 0x9999999999999999 20 | x = lib.close(-42) 21 | assert x == -1 22 | 23 | print(lib.stdout) 24 | 25 | print(ffi.new("struct point_s *")) 26 | print(ffi.offsetof("struct point_s", "x")) 27 | print(ffi.offsetof("struct point_s", "y")) 28 | print(ffi.new("struct point_s[CC]")) 29 | assert ffi.sizeof("struct point_s[CC]") == 2 * ffi.sizeof("struct point_s") 30 | 31 | print(ffi.cast("enum myenum_e", 2)) 32 | print(ffi.cast("myint_t", -2)) 33 | assert ffi.typeof("myint_t") == ffi.typeof("int") 34 | 35 | del ffi, lib 36 | -------------------------------------------------------------------------------- /demo/pwuid.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys, os 3 | 4 | # run pwuid_build first, then make sure the shared object is on sys.path 5 | from _pwuid_cffi import ffi, lib 6 | 7 | 8 | print(ffi.string(lib.getpwuid(0).pw_name)) 9 | -------------------------------------------------------------------------------- /demo/pwuid_build.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | ffi = FFI() 3 | ffi.cdef(""" // some declarations from the man page 4 | struct passwd { 5 | char *pw_name; 6 | ...; 7 | }; 8 | struct passwd *getpwuid(int uid); 9 | """) 10 | 11 | ffi.set_source('_pwuid_cffi', """ // passed to the real C compiler 12 | #include 13 | #include 14 | """) 15 | 16 | 17 | if __name__ == '__main__': 18 | ffi.compile() 19 | -------------------------------------------------------------------------------- /demo/py.cleanup: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import sys, os, stat 3 | from bsdopendirtype import opendir 4 | 5 | def clean(path): 6 | global count 7 | try: 8 | content = opendir(path) 9 | except OSError: 10 | print >> sys.stderr, "skipping", path 11 | return 12 | for filename, smode in content: 13 | if stat.S_ISDIR(smode): 14 | clean(filename) 15 | if filename.endswith('/__pycache__'): 16 | try: 17 | os.rmdir(filename) 18 | except OSError: 19 | pass 20 | elif (filename.endswith('.pyc') or filename.endswith('.pyo') or 21 | filename.endswith('.pyc~') or filename.endswith('.pyo~')): 22 | os.unlink(filename) 23 | count += 1 24 | 25 | count = 0 26 | 27 | for arg in sys.argv[1:] or ['.']: 28 | print "cleaning path", arg, "of .pyc/.pyo/__pycache__ files" 29 | clean(arg) 30 | 31 | print "%d files removed" % (count,) 32 | -------------------------------------------------------------------------------- /demo/pyobj.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | referents = [] # list "object descriptor -> python object" 4 | freelist = None 5 | 6 | def store(x): 7 | "Store the object 'x' and returns a new object descriptor for it." 8 | global freelist 9 | p = freelist 10 | if p is None: 11 | p = len(referents) 12 | referents.append(x) 13 | else: 14 | freelist = referents[p] 15 | referents[p] = x 16 | return p 17 | 18 | def discard(p): 19 | """Discard (i.e. close) the object descriptor 'p'. 20 | Return the original object that was attached to 'p'.""" 21 | global freelist 22 | x = referents[p] 23 | referents[p] = freelist 24 | freelist = p 25 | return x 26 | 27 | class Ref(object): 28 | """For use in 'with Ref(x) as ob': open an object descriptor 29 | and returns it in 'ob', and close it automatically when the 30 | 'with' statement finishes.""" 31 | def __init__(self, x): 32 | self.x = x 33 | def __enter__(self): 34 | self.p = p = store(self.x) 35 | return p 36 | def __exit__(self, *args): 37 | discard(self.p) 38 | 39 | def count_pyobj_alive(): 40 | result = len(referents) 41 | p = freelist 42 | while p is not None: 43 | assert result > 0 44 | result -= 1 45 | p = referents[p] 46 | return result 47 | 48 | # ------------------------------------------------------------ 49 | 50 | if __name__ == '__main__': 51 | import api 52 | 53 | ffi = api.PythonFFI() 54 | 55 | ffi.cdef(""" 56 | typedef int pyobj_t; 57 | int sum_integers(pyobj_t p_list); 58 | pyobj_t sum_objects(pyobj_t p_list, pyobj_t p_initial); 59 | """) 60 | 61 | @ffi.pyexport("int(pyobj_t)") 62 | def length(p_list): 63 | list = referents[p_list] 64 | return len(list) 65 | 66 | @ffi.pyexport("int(pyobj_t, int)") 67 | def getitem(p_list, index): 68 | list = referents[p_list] 69 | return list[index] 70 | 71 | @ffi.pyexport("pyobj_t(pyobj_t)") 72 | def pyobj_dup(p): 73 | return store(referents[p]) 74 | 75 | @ffi.pyexport("void(pyobj_t)") 76 | def pyobj_close(p): 77 | discard(p) 78 | 79 | @ffi.pyexport("pyobj_t(pyobj_t, int)") 80 | def pyobj_getitem(p_list, index): 81 | list = referents[p_list] 82 | return store(list[index]) 83 | 84 | @ffi.pyexport("pyobj_t(pyobj_t, pyobj_t)") 85 | def pyobj_add(p1, p2): 86 | return store(referents[p1] + referents[p2]) 87 | 88 | lib = ffi.verify(""" 89 | typedef int pyobj_t; /* an "object descriptor" number */ 90 | 91 | int sum_integers(pyobj_t p_list) { 92 | /* this a demo function written in C, using the API 93 | defined above: length() and getitem(). */ 94 | int i, result = 0; 95 | int count = length(p_list); 96 | for (i=0; i 31 | #include 32 | #include 33 | """) 34 | 35 | if __name__ == '__main__': 36 | ffi.compile() 37 | -------------------------------------------------------------------------------- /demo/readdir2_setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | import readdir2_build 3 | 4 | setup( 5 | name="readdir2", 6 | version="0.1", 7 | py_modules=["readdir2"], 8 | ext_modules=[readdir2_build.ffi.distutils_extension('build')], 9 | ) 10 | -------------------------------------------------------------------------------- /demo/readdir_build.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from cffi import FFI 3 | 4 | if not sys.platform.startswith('linux'): 5 | raise Exception("Linux-only demo") 6 | 7 | 8 | ffi = FFI() 9 | ffi.cdef(""" 10 | 11 | typedef void DIR; 12 | typedef long ino_t; 13 | typedef long off_t; 14 | 15 | struct dirent { 16 | ino_t d_ino; /* inode number */ 17 | off_t d_off; /* offset to the next dirent */ 18 | unsigned short d_reclen; /* length of this record */ 19 | unsigned char d_type; /* type of file; not supported 20 | by all file system types */ 21 | char d_name[256]; /* filename */ 22 | }; 23 | 24 | int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); 25 | int openat(int dirfd, const char *pathname, int flags); 26 | DIR *fdopendir(int fd); 27 | int closedir(DIR *dirp); 28 | 29 | """) 30 | ffi.set_source("_readdir", None) 31 | 32 | if __name__ == '__main__': 33 | ffi.compile() 34 | -------------------------------------------------------------------------------- /demo/readdir_ctypes.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | # A Linux-only demo 3 | # 4 | # For comparison purposes, this is a ctypes version of readdir.py. 5 | import sys 6 | import ctypes 7 | 8 | if not sys.platform.startswith('linux'): 9 | raise Exception("Linux-only demo") 10 | 11 | 12 | DIR_p = ctypes.c_void_p 13 | ino_t = ctypes.c_long 14 | off_t = ctypes.c_long 15 | 16 | class DIRENT(ctypes.Structure): 17 | _fields_ = [ 18 | ('d_ino', ino_t), # inode number 19 | ('d_off', off_t), # offset to the next dirent 20 | ('d_reclen', ctypes.c_ushort), # length of this record 21 | ('d_type', ctypes.c_ubyte), # type of file; not supported 22 | # by all file system types 23 | ('d_name', ctypes.c_char * 256), # filename 24 | ] 25 | DIRENT_p = ctypes.POINTER(DIRENT) 26 | DIRENT_pp = ctypes.POINTER(DIRENT_p) 27 | 28 | C = ctypes.CDLL(None) 29 | 30 | readdir_r = C.readdir_r 31 | readdir_r.argtypes = [DIR_p, DIRENT_p, DIRENT_pp] 32 | readdir_r.restype = ctypes.c_int 33 | 34 | openat = C.openat 35 | openat.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] 36 | openat.restype = ctypes.c_int 37 | 38 | fdopendir = C.fdopendir 39 | fdopendir.argtypes = [ctypes.c_int] 40 | fdopendir.restype = DIR_p 41 | 42 | closedir = C.closedir 43 | closedir.argtypes = [DIR_p] 44 | closedir.restype = ctypes.c_int 45 | 46 | 47 | def walk(basefd, path): 48 | print('{', path) 49 | dirfd = openat(basefd, path, 0) 50 | if dirfd < 0: 51 | # error in openat() 52 | return 53 | dir = fdopendir(dirfd) 54 | dirent = DIRENT() 55 | result = DIRENT_p() 56 | while True: 57 | if readdir_r(dir, dirent, result): 58 | # error in readdir_r() 59 | break 60 | if not result: 61 | break 62 | name = dirent.d_name 63 | print('%3d %s' % (dirent.d_type, name)) 64 | if dirent.d_type == 4 and name != '.' and name != '..': 65 | walk(dirfd, name) 66 | closedir(dir) 67 | print('}') 68 | 69 | 70 | walk(-1, "/tmp") 71 | -------------------------------------------------------------------------------- /demo/readdir_setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="example", 5 | version="0.1", 6 | py_modules=["readdir"], 7 | setup_requires=["cffi>=1.0.dev0"], 8 | cffi_modules=["readdir_build.py:ffi"], 9 | install_requires=["cffi>=1.0.dev0"], 10 | zip_safe=False, 11 | ) 12 | -------------------------------------------------------------------------------- /demo/recopendirtype.py: -------------------------------------------------------------------------------- 1 | import os 2 | from _recopendirtype import ffi, lib 3 | 4 | 5 | def _posix_error(): 6 | raise OSError(ffi.errno, os.strerror(ffi.errno)) 7 | 8 | _dtype_to_smode = { 9 | lib.DT_BLK: 0o060000, 10 | lib.DT_CHR: 0o020000, 11 | lib.DT_DIR: 0o040000, 12 | lib.DT_FIFO: 0o010000, 13 | lib.DT_LNK: 0o120000, 14 | lib.DT_REG: 0o100000, 15 | lib.DT_SOCK: 0o140000, 16 | } 17 | 18 | def opendir(dir): 19 | if len(dir) == 0: 20 | dir = b'.' 21 | dirname = dir 22 | if not dirname.endswith(b'/'): 23 | dirname += b'/' 24 | dirp = lib.opendir(dir) 25 | if dirp == ffi.NULL: 26 | raise _posix_error() 27 | dirent = ffi.new("struct dirent *") 28 | result = ffi.new("struct dirent **") 29 | try: 30 | while True: 31 | ffi.errno = 0 32 | err = lib.readdir_r(dirp, dirent, result) 33 | if err: # really got an error 34 | raise OSError(err, os.strerror(err)) 35 | if result[0] == ffi.NULL: 36 | return # 37 | name = ffi.string(dirent.d_name) 38 | if name == b'.' or name == b'..': 39 | continue 40 | name = dirname + name 41 | try: 42 | smode = _dtype_to_smode[dirent.d_type] 43 | except KeyError: 44 | smode = os.lstat(name).st_mode 45 | yield name, smode 46 | finally: 47 | lib.closedir(dirp) 48 | 49 | if __name__ == '__main__': 50 | for name, smode in opendir(b'/tmp'): 51 | print(hex(smode), name) 52 | -------------------------------------------------------------------------------- /demo/recopendirtype_build.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | import bsdopendirtype_build 3 | 4 | ffi = FFI() 5 | 6 | # ========== This is a demo of ffi.include() ========== 7 | ffi.include(bsdopendirtype_build.ffi) 8 | 9 | ffi.cdef(""" 10 | int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); 11 | """) 12 | 13 | ffi.set_source("_recopendirtype", """ 14 | #include 15 | #include 16 | """) 17 | 18 | if __name__ == '__main__': 19 | ffi.compile() 20 | -------------------------------------------------------------------------------- /demo/setup_manual.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | setup(name='manual', 4 | ext_modules=[Extension(name='manual', 5 | sources=['manual.c'])]) 6 | -------------------------------------------------------------------------------- /demo/winclipboard.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | __author__ = "Israel Fruchter " 3 | 4 | import sys, os 5 | 6 | if not sys.platform == 'win32': 7 | raise Exception("Windows-only demo") 8 | 9 | try: 10 | from _winclipboard_cffi import ffi, lib 11 | except ImportError: 12 | print('run winclipboard_build first, then make sure the shared object is on sys.path') 13 | sys.exit(1) 14 | 15 | # ffi "knows" about the declared variables and functions from the 16 | # cdef parts of the module _winclipboard_cffi created, 17 | # lib "knows" how to call the functions from the set_source parts 18 | # of the module. 19 | 20 | def CopyToClipboard(string): 21 | ''' 22 | use win32 api to copy `string` to the clipboard 23 | ''' 24 | hWnd = lib.GetConsoleWindow() 25 | 26 | if lib.OpenClipboard(hWnd): 27 | cstring = ffi.new("char[]", string) 28 | size = ffi.sizeof(cstring) 29 | 30 | # make it a moveable memory for other processes 31 | hGlobal = lib.GlobalAlloc(lib.GMEM_MOVEABLE, size) 32 | buffer = lib.GlobalLock(hGlobal) 33 | lib.memcpy(buffer, cstring, size) 34 | lib.GlobalUnlock(hGlobal) 35 | 36 | res = lib.EmptyClipboard() 37 | res = lib.SetClipboardData(lib.CF_TEXT, buffer) 38 | 39 | lib.CloseClipboard() 40 | 41 | CopyToClipboard("hello world from cffi") 42 | -------------------------------------------------------------------------------- /demo/winclipboard_build.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | 3 | ffi = FFI() 4 | ffi.cdef(''' 5 | typedef void * HANDLE; 6 | typedef HANDLE HWND; 7 | typedef int BOOL; 8 | typedef unsigned int UINT; 9 | typedef int SIZE_T; 10 | typedef char * LPTSTR; 11 | typedef HANDLE HGLOBAL; 12 | typedef HANDLE LPVOID; 13 | 14 | HWND GetConsoleWindow(void); 15 | 16 | LPVOID GlobalLock( HGLOBAL hMem ); 17 | BOOL GlobalUnlock( HGLOBAL hMem ); 18 | HGLOBAL GlobalAlloc(UINT uFlags, SIZE_T dwBytes); 19 | 20 | BOOL OpenClipboard(HWND hWndNewOwner); 21 | BOOL CloseClipboard(void); 22 | BOOL EmptyClipboard(void); 23 | HANDLE SetClipboardData(UINT uFormat, HANDLE hMem); 24 | 25 | #define CF_TEXT ... 26 | #define GMEM_MOVEABLE ... 27 | 28 | void * memcpy(void * s1, void * s2, int n); 29 | ''') 30 | 31 | ffi.set_source('_winclipboard_cffi', ''' 32 | #include 33 | ''', libraries=["user32"]) 34 | 35 | if __name__ == '__main__': 36 | ffi.compile() 37 | -------------------------------------------------------------------------------- /demo/xclient.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | 3 | # run xclient_build first, then make sure the shared object is on sys.path 4 | from _xclient_cffi import ffi, lib 5 | 6 | 7 | # ffi "knows" about the declared variables and functions from the 8 | # cdef parts of the module xclient_build created, 9 | # lib "knows" how to call the functions from the set_source parts 10 | # of the module. 11 | 12 | 13 | class XError(Exception): 14 | pass 15 | 16 | def main(): 17 | display = lib.XOpenDisplay(ffi.NULL) 18 | if display == ffi.NULL: 19 | raise XError("cannot open display") 20 | w = lib.XCreateSimpleWindow(display, lib.DefaultRootWindow(display), 21 | 10, 10, 500, 350, 0, 0, 0) 22 | lib.XMapRaised(display, w) 23 | event = ffi.new("XEvent *") 24 | lib.XNextEvent(display, event) 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /demo/xclient_build.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | ffi = FFI() 3 | ffi.cdef(""" 4 | 5 | typedef ... Display; 6 | typedef struct { ...; } Window; 7 | 8 | typedef struct { int type; ...; } XEvent; 9 | 10 | Display *XOpenDisplay(char *display_name); 11 | Window DefaultRootWindow(Display *display); 12 | int XMapRaised(Display *display, Window w); 13 | Window XCreateSimpleWindow(Display *display, Window parent, int x, int y, 14 | unsigned int width, unsigned int height, 15 | unsigned int border_width, unsigned long border, 16 | unsigned long background); 17 | int XNextEvent(Display *display, XEvent *event_return); 18 | """) 19 | 20 | ffi.set_source('_xclient_cffi', """ 21 | #include 22 | """, libraries=['X11']) 23 | 24 | if __name__ == '__main__': 25 | ffi.compile(verbose=True) 26 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CFFI.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CFFI.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | set SPHINXBUILD=sphinx-build 6 | set BUILDDIR=build 7 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 8 | if NOT "%PAPER%" == "" ( 9 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 10 | ) 11 | 12 | if "%1" == "" goto help 13 | 14 | if "%1" == "help" ( 15 | :help 16 | echo.Please use `make ^` where ^ is one of 17 | echo. html to make standalone HTML files 18 | echo. dirhtml to make HTML files named index.html in directories 19 | echo. pickle to make pickle files 20 | echo. json to make JSON files 21 | echo. htmlhelp to make HTML files and a HTML help project 22 | echo. qthelp to make HTML files and a qthelp project 23 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 24 | echo. changes to make an overview over all changed/added/deprecated items 25 | echo. linkcheck to check all external links for integrity 26 | echo. doctest to run all doctests embedded in the documentation if enabled 27 | goto end 28 | ) 29 | 30 | if "%1" == "clean" ( 31 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 32 | del /q /s %BUILDDIR%\* 33 | goto end 34 | ) 35 | 36 | if "%1" == "html" ( 37 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 38 | echo. 39 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 40 | goto end 41 | ) 42 | 43 | if "%1" == "dirhtml" ( 44 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 45 | echo. 46 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 47 | goto end 48 | ) 49 | 50 | if "%1" == "pickle" ( 51 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 52 | echo. 53 | echo.Build finished; now you can process the pickle files. 54 | goto end 55 | ) 56 | 57 | if "%1" == "json" ( 58 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 59 | echo. 60 | echo.Build finished; now you can process the JSON files. 61 | goto end 62 | ) 63 | 64 | if "%1" == "htmlhelp" ( 65 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 66 | echo. 67 | echo.Build finished; now you can run HTML Help Workshop with the ^ 68 | .hhp project file in %BUILDDIR%/htmlhelp. 69 | goto end 70 | ) 71 | 72 | if "%1" == "qthelp" ( 73 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 74 | echo. 75 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 76 | .qhcp project file in %BUILDDIR%/qthelp, like this: 77 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\CFFI.qhcp 78 | echo.To view the help file: 79 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\CFFI.ghc 80 | goto end 81 | ) 82 | 83 | if "%1" == "latex" ( 84 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 85 | echo. 86 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 87 | goto end 88 | ) 89 | 90 | if "%1" == "changes" ( 91 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 92 | echo. 93 | echo.The overview file is in %BUILDDIR%/changes. 94 | goto end 95 | ) 96 | 97 | if "%1" == "linkcheck" ( 98 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 99 | echo. 100 | echo.Link check complete; look for any errors in the above output ^ 101 | or in %BUILDDIR%/linkcheck/output.txt. 102 | goto end 103 | ) 104 | 105 | if "%1" == "doctest" ( 106 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 107 | echo. 108 | echo.Testing of doctests in the sources finished, look at the ^ 109 | results in %BUILDDIR%/doctest/output.txt. 110 | goto end 111 | ) 112 | 113 | :end 114 | -------------------------------------------------------------------------------- /doc/misc/design.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Design decisions 3 | ================ 4 | 5 | * Generally follow `LuaJIT's ffi`_. 6 | 7 | * Be explicit: almost no automatic conversions. Here is the set 8 | of automatic conversions: the various C integer types are 9 | automatically wrapped and unwrapped to regular applevel integers. The 10 | type ``char`` might correspond to single-character strings instead; 11 | for integer correspondence you would use ``signed char`` or ``unsigned 12 | char``. We might also decide that ``const char *`` automatically maps 13 | to strings; for cases where you don't want that, use ``char *``. 14 | 15 | * Integers are not automatically converted when passed as vararg 16 | arguments. You have to use explicitly ``ffi.new("int", 42)`` or 17 | ``ffi.new("long", 42)`` to resolve the ambiguity. Floats would be 18 | fine (varargs in C can only accept ``double``, not ``float``), but 19 | there is again ambiguity between characters and strings. Even with 20 | floats the result is a bit strange because passing a float works 21 | but passing an integer not. I would fix this once and for all by 22 | saying that varargs must *always* be a cdata (from ``ffi.new()``). 23 | The possibly acceptable exception would be None (for ``NULL``). 24 | 25 | * The internal class ``blob`` is used for raw-malloced data. You only 26 | get a class that has internally a ``blob`` instance (or maybe is a 27 | subclass of ``blob``) by calling ``ffi.new(struct-or-array-type)``. 28 | The other cases, namely the cases where the type is a pointer or a 29 | primitive, don't need a blob because it's not possible to take their 30 | raw address. 31 | 32 | * It would be possible to add a debug mode: when we cast ``struct foo`` 33 | to ``struct foo *`` or store it in some other struct, then we would 34 | additionally record a weakref to the original ``struct foo`` blob. 35 | If later we try to access the ``struct foo *`` but the weakref shows 36 | that the blob was freed, we complain. This is a difference with 37 | ctypes, which in these cases would store a strong reference and 38 | keep the blob alive. "Explicit is better than implicit", so we ask 39 | the user to keep a reference to the original blob alive as long as 40 | it may be used (instead of doing the right things in 90% of the cases 41 | but still crashing in the remaining 10%). 42 | 43 | * LuaJIT uses ``struct foo &`` for a number of things, like for ``p[0]`` 44 | if ``p`` is a ``struct foo *``. I suppose it's not a bad idea at least 45 | to have internally such types, even if you can't specify them through 46 | pycparser. Basically ``struct foo &`` is a type that doesn't own a 47 | blob, whereas ``struct foo`` is the type that does. 48 | 49 | * LuaJIT uses ``int[?]`` which pycparser doesn't accept. I propose 50 | instead to use ``int[]`` for the same purpose (its use is anyway quite 51 | close to the C standard's use of ``int[]``). 52 | 53 | 54 | .. _LuaJIT's ffi: https://luajit.org/ext_ffi.html 55 | -------------------------------------------------------------------------------- /doc/misc/parse_c_type.rst: -------------------------------------------------------------------------------- 1 | ================================================== 2 | CPython C extension module produced by recompile() 3 | ================================================== 4 | 5 | Global variable:: 6 | 7 | _cffi_opcode_t _cffi_types[]; 8 | 9 | Every ``_cffi_types`` entry is initially an odd integer. At runtime, it 10 | is fixed to be a ``CTypeDescrObject *`` when the odd integer is 11 | interpreted and turned into a real ```` object. 12 | 13 | The generated C functions are listed in ``_cffi_globals``, a sorted array 14 | of entries which get turned lazily into real ````. Each entry in this array has an index in the ``_cffi_types`` 16 | array, which describe the function type (``OP_FUNCTION`` opcode, see 17 | below). We turn the odd integers describing argument and return types 18 | into real CTypeDescrObjects at the point where the entry is turned 19 | into a real builtin function object. 20 | 21 | The odd integers are "opcodes" that contain a type info in the lowest 22 | byte. The remaining high bytes of the integer is an "arg" that depends 23 | on the type info: 24 | 25 | OP_PRIMITIVE 26 | the arg tells which primitive type it is (an index in some list) 27 | 28 | OP_POINTER 29 | the arg is the index of the item type in the ``_cffi_types`` array. 30 | 31 | OP_ARRAY 32 | the arg is the index of the item type in the ``_cffi_types`` array. 33 | followed by another opcode that contains (uintptr_t)length_of_array. 34 | 35 | OP_OPEN_ARRAY 36 | for syntax like "int[]". same as OP_ARRAY but without the length 37 | 38 | OP_STRUCT_UNION 39 | the arg is the index of the struct/union in ``_cffi_structs_unions`` 40 | 41 | OP_ENUM 42 | the arg is the index of the enum in ``_cffi_enums`` 43 | 44 | OP_TYPENAME 45 | the arg is the index of the typename in ``_cffi_typenames`` 46 | 47 | OP_FUNCTION 48 | the arg is the index of the result type in ``_cffi_types``. 49 | followed by other opcodes for the arguments. 50 | terminated by ``OP_FUNCTION_END``. 51 | 52 | OP_FUNCTION_END 53 | the arg's lowest bit is set if there is a "..." argument. 54 | 55 | OP_NOOP 56 | simple indirection: the arg is the index to look further in 57 | 58 | There are other opcodes, used not inside ``_cffi_types`` but in other 59 | individual ``type_op`` fields. Most importantly, these are used 60 | on ``_cffi_globals`` entries: 61 | 62 | OP_CPYTHON_BLTN_* 63 | declare a function 64 | 65 | OP_CONSTANT 66 | declare a non-integral constant 67 | 68 | OP_CONSTANT_INT 69 | declare an int constant 70 | 71 | OP_GLOBAL_VAR 72 | declare a global var 73 | -------------------------------------------------------------------------------- /doc/source/goals.rst: -------------------------------------------------------------------------------- 1 | Goals 2 | ----- 3 | 4 | The interface is based on `LuaJIT's FFI`_, and follows a few principles: 5 | 6 | * The goal is to call C code from Python without learning a 3rd language: 7 | existing alternatives require users to learn domain specific language 8 | (Cython_, SWIG_) or API (ctypes_). The CFFI design requires users to know 9 | only C and Python, minimizing the extra bits of API that need to be learned. 10 | 11 | * Keep all the Python-related logic in Python so that you don't need to 12 | write much C code (unlike `CPython native C extensions`_). 13 | 14 | * The preferred way is to work at the level of the API (Application 15 | Programming Interface): the C compiler is called from the declarations 16 | you write to validate and link to the C language constructs. 17 | Alternatively, it is also possible to work at the ABI level 18 | (Application Binary Interface), the way ctypes_ work. 19 | However, on non-Windows platforms, C libraries typically 20 | have a specified C API but not an ABI (e.g. they may 21 | document a "struct" as having at least these fields, but maybe more). 22 | 23 | * Try to be complete. For now some C99 constructs are not supported, 24 | but all C89 should be, including macros (and including macro "abuses", 25 | which you can `manually wrap`_ in saner-looking C functions). 26 | 27 | * Attempt to support both PyPy and CPython, with a reasonable path 28 | for other Python implementations like IronPython and Jython. 29 | 30 | * Note that this project is **not** about embedding executable C code in 31 | Python, unlike `Weave`_. This is about calling existing C libraries 32 | from Python. 33 | 34 | * There is no C++ support. Sometimes, it is reasonable to write a C 35 | wrapper around the C++ code and then call this C API with CFFI. 36 | Otherwise, look at other projects. I would recommend cppyy_, which 37 | has got some similarities (and also works efficiently on both CPython 38 | and PyPy). 39 | 40 | .. _`LuaJIT's FFI`: http://luajit.org/ext_ffi.html 41 | .. _`Cython`: http://www.cython.org 42 | .. _`SWIG`: http://www.swig.org/ 43 | .. _`CPython native C extensions`: http://docs.python.org/extending/extending.html 44 | .. _`native C extensions`: http://docs.python.org/extending/extending.html 45 | .. _`ctypes`: http://docs.python.org/library/ctypes.html 46 | .. _`Weave`: http://wiki.scipy.org/Weave 47 | .. _`cppyy`: http://cppyy.readthedocs.io/en/latest/ 48 | .. _`manually wrap`: overview.html#abi-versus-api 49 | 50 | Get started by reading `the overview`__. 51 | 52 | .. __: overview.html 53 | 54 | 55 | Comments and bugs 56 | ----------------- 57 | 58 | The best way to contact us is on the IRC ``#cffi`` or ``#pypy`` channels of 59 | ``irc.libera.chat``. Feel free to discuss matters either there or in 60 | the `mailing list`_. Please report to the `issue tracker`_ any bugs. 61 | 62 | As a general rule, when there is a design issue to resolve, we pick the 63 | solution that is the "most C-like". We hope that this module has got 64 | everything you need to access C code and nothing more. 65 | 66 | --- the authors, Armin Rigo and Maciej Fijalkowski 67 | 68 | .. _`issue tracker`: https://github.com/python-cffi/cffi/issues 69 | .. _`mailing list`: https://groups.google.com/forum/#!forum/python-cffi 70 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | ================================ 2 | CFFI documentation 3 | ================================ 4 | 5 | C Foreign Function Interface for Python. Interact with almost any C 6 | code from Python, based on C-like declarations that you can often 7 | copy-paste from header files or documentation. 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | goals 13 | whatsnew 14 | installation 15 | overview 16 | using 17 | ref 18 | cdef 19 | embedding 20 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | # first version that supports Python 3.12; older versions may work 4 | # with previous Python versions, but are not tested 5 | "setuptools >= 66.1" 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [project] 10 | name = "cffi" 11 | version = "1.18.0.dev0" 12 | dependencies = [ 13 | "pycparser; implementation_name != 'PyPy'", 14 | ] 15 | requires-python = ">=3.8" 16 | 17 | description = "Foreign Function Interface for Python calling C code." 18 | readme = {file = "README.md", content-type = "text/markdown"} 19 | classifiers = [ 20 | "Programming Language :: Python", 21 | "Programming Language :: Python :: 3", 22 | "Programming Language :: Python :: 3.8", 23 | "Programming Language :: Python :: 3.9", 24 | "Programming Language :: Python :: 3.10", 25 | "Programming Language :: Python :: 3.11", 26 | "Programming Language :: Python :: 3.12", 27 | "Programming Language :: Python :: 3.13", 28 | "Programming Language :: Python :: Implementation :: CPython", 29 | "Programming Language :: Python :: Implementation :: PyPy", 30 | "License :: OSI Approved :: MIT License", 31 | ] 32 | authors = [ 33 | {name = "Armin Rigo"}, 34 | {name = "Maciej Fijalkowski"}, 35 | ] 36 | maintainers = [ 37 | {name = "Matt Davis"}, 38 | {name = "Matt Clay"}, 39 | ] 40 | 41 | [project.entry-points."distutils.setup_keywords"] 42 | cffi_modules = "cffi.setuptools_ext:cffi_modules" 43 | 44 | [project.urls] 45 | Documentation = "https://cffi.readthedocs.io/" 46 | Changelog = "https://cffi.readthedocs.io/en/latest/whatsnew.html" 47 | Downloads = "https://github.com/python-cffi/cffi/releases" 48 | Contact = "https://groups.google.com/forum/#!forum/python-cffi" 49 | "Source Code" = "https://github.com/python-cffi/cffi" 50 | "Issue Tracker" = "https://github.com/python-cffi/cffi/issues" 51 | -------------------------------------------------------------------------------- /setup_base.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | 3 | 4 | from setup import include_dirs, sources, libraries, define_macros 5 | from setup import library_dirs, extra_compile_args, extra_link_args 6 | 7 | 8 | if __name__ == '__main__': 9 | from distutils.core import setup 10 | from distutils.extension import Extension 11 | 12 | standard = '__pypy__' not in sys.builtin_module_names 13 | setup(packages=['cffi'], 14 | requires=['pycparser'], 15 | ext_modules=[Extension(name = '_cffi_backend', 16 | include_dirs=include_dirs, 17 | sources=sources, 18 | libraries=libraries, 19 | define_macros=define_macros, 20 | library_dirs=library_dirs, 21 | extra_compile_args=extra_compile_args, 22 | extra_link_args=extra_link_args, 23 | )] * standard) 24 | -------------------------------------------------------------------------------- /src/c/cglob.c: -------------------------------------------------------------------------------- 1 | 2 | typedef void *(*gs_fetch_addr_fn)(void); 3 | 4 | typedef struct { 5 | PyObject_HEAD 6 | 7 | PyObject *gs_name; 8 | CTypeDescrObject *gs_type; 9 | char *gs_data; 10 | gs_fetch_addr_fn gs_fetch_addr; 11 | 12 | } GlobSupportObject; 13 | 14 | static void glob_support_dealloc(GlobSupportObject *gs) 15 | { 16 | Py_DECREF(gs->gs_name); 17 | Py_DECREF(gs->gs_type); 18 | PyObject_Del(gs); 19 | } 20 | 21 | static PyTypeObject GlobSupport_Type = { 22 | PyVarObject_HEAD_INIT(NULL, 0) 23 | "_cffi_backend.__FFIGlobSupport", 24 | sizeof(GlobSupportObject), 25 | 0, 26 | (destructor)glob_support_dealloc, /* tp_dealloc */ 27 | 0, /* tp_print */ 28 | 0, /* tp_getattr */ 29 | 0, /* tp_setattr */ 30 | 0, /* tp_compare */ 31 | 0, /* tp_repr */ 32 | 0, /* tp_as_number */ 33 | 0, /* tp_as_sequence */ 34 | 0, /* tp_as_mapping */ 35 | 0, /* tp_hash */ 36 | 0, /* tp_call */ 37 | 0, /* tp_str */ 38 | PyObject_GenericGetAttr, /* tp_getattro */ 39 | 0, /* tp_setattro */ 40 | 0, /* tp_as_buffer */ 41 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 42 | }; 43 | 44 | #define GlobSupport_Check(ob) (Py_TYPE(ob) == &GlobSupport_Type) 45 | 46 | static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type, 47 | char *addr, gs_fetch_addr_fn fetch_addr) 48 | { 49 | GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type); 50 | if (gs == NULL) 51 | return NULL; 52 | 53 | Py_INCREF(name); 54 | Py_INCREF(type); 55 | gs->gs_name = name; 56 | gs->gs_type = type; 57 | gs->gs_data = addr; 58 | gs->gs_fetch_addr = fetch_addr; 59 | return (PyObject *)gs; 60 | } 61 | 62 | static void *fetch_global_var_addr(GlobSupportObject *gs) 63 | { 64 | void *data; 65 | if (gs->gs_data != NULL) { 66 | data = gs->gs_data; 67 | } 68 | else { 69 | Py_BEGIN_ALLOW_THREADS 70 | restore_errno(); 71 | data = gs->gs_fetch_addr(); 72 | save_errno(); 73 | Py_END_ALLOW_THREADS 74 | } 75 | if (data == NULL) { 76 | PyErr_Format(FFIError, "global variable '%s' is at address NULL", 77 | PyText_AS_UTF8(gs->gs_name)); 78 | return NULL; 79 | } 80 | return data; 81 | } 82 | 83 | static PyObject *read_global_var(GlobSupportObject *gs) 84 | { 85 | void *data = fetch_global_var_addr(gs); 86 | if (data == NULL) 87 | return NULL; 88 | return convert_to_object(data, gs->gs_type); 89 | } 90 | 91 | static int write_global_var(GlobSupportObject *gs, PyObject *obj) 92 | { 93 | void *data = fetch_global_var_addr(gs); 94 | if (data == NULL) 95 | return -1; 96 | return convert_from_object(data, gs->gs_type, obj); 97 | } 98 | 99 | static PyObject *cg_addressof_global_var(GlobSupportObject *gs) 100 | { 101 | void *data; 102 | PyObject *x, *ptrtype = new_pointer_type(gs->gs_type); 103 | if (ptrtype == NULL) 104 | return NULL; 105 | 106 | data = fetch_global_var_addr(gs); 107 | if (data != NULL) 108 | x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype); 109 | else 110 | x = NULL; 111 | Py_DECREF(ptrtype); 112 | return x; 113 | } 114 | -------------------------------------------------------------------------------- /src/c/file_emulator.h: -------------------------------------------------------------------------------- 1 | 2 | /* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */ 3 | 4 | static PyObject *PyIOBase_TypeObj; 5 | 6 | static int init_file_emulator(void) 7 | { 8 | if (PyIOBase_TypeObj == NULL) { 9 | PyObject *io = PyImport_ImportModule("_io"); 10 | if (io == NULL) 11 | return -1; 12 | PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase"); 13 | if (PyIOBase_TypeObj == NULL) 14 | return -1; 15 | } 16 | return 0; 17 | } 18 | 19 | 20 | #define PyFile_Check(p) PyObject_IsInstance(p, PyIOBase_TypeObj) 21 | 22 | 23 | static void _close_file_capsule(PyObject *ob_capsule) 24 | { 25 | FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE"); 26 | if (f != NULL) 27 | fclose(f); 28 | } 29 | 30 | 31 | static FILE *PyFile_AsFile(PyObject *ob_file) 32 | { 33 | PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL; 34 | FILE *f; 35 | int fd; 36 | const char *mode; 37 | 38 | ob = PyObject_CallMethod(ob_file, "flush", NULL); 39 | if (ob == NULL) 40 | goto fail; 41 | Py_DECREF(ob); 42 | 43 | ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE"); 44 | if (ob_capsule == NULL) { 45 | PyErr_Clear(); 46 | 47 | fd = PyObject_AsFileDescriptor(ob_file); 48 | if (fd < 0) 49 | goto fail; 50 | 51 | ob_mode = PyObject_GetAttrString(ob_file, "mode"); 52 | if (ob_mode == NULL) 53 | goto fail; 54 | mode = PyText_AsUTF8(ob_mode); 55 | if (mode == NULL) 56 | goto fail; 57 | 58 | fd = dup(fd); 59 | if (fd < 0) { 60 | PyErr_SetFromErrno(PyExc_OSError); 61 | goto fail; 62 | } 63 | 64 | f = fdopen(fd, mode); 65 | if (f == NULL) { 66 | close(fd); 67 | PyErr_SetFromErrno(PyExc_OSError); 68 | goto fail; 69 | } 70 | setbuf(f, NULL); /* non-buffered */ 71 | Py_DECREF(ob_mode); 72 | ob_mode = NULL; 73 | 74 | ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule); 75 | if (ob_capsule == NULL) { 76 | fclose(f); 77 | goto fail; 78 | } 79 | 80 | if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0) 81 | goto fail; 82 | } 83 | else { 84 | f = PyCapsule_GetPointer(ob_capsule, "FILE"); 85 | } 86 | Py_DECREF(ob_capsule); /* assumes still at least one reference */ 87 | return f; 88 | 89 | fail: 90 | Py_XDECREF(ob_mode); 91 | Py_XDECREF(ob_capsule); 92 | return NULL; 93 | } 94 | -------------------------------------------------------------------------------- /src/c/libffi_arm64/README: -------------------------------------------------------------------------------- 1 | Please run build_libffi.bat to generate the libffi static library and header files 2 | 3 | Environment variable LIBFFI_SOURCE needs to be set to libffi source before invoking the script. 4 | 5 | Libffi source can be downloaded from https://github.com/libffi/libffi -------------------------------------------------------------------------------- /src/c/libffi_arm64/build_libffi.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | goto :Run 3 | 4 | :Usage 5 | echo. 6 | echo Before running prepare_libffi.bat 7 | echo . 8 | echo LIBFFI_SOURCE environment variable must be set to the location of libffi source 9 | echo Source can be checked out from https://github.com/libffi/libffi 10 | echo. 11 | echo Cygwin needs to be installed (Invoke with --install-cygwin to install) 12 | echo. 13 | echo. Visual Studio 2017 or newer with ARM64 toolchain needs to be installed 14 | :Run 15 | set INSTALL_CYGWIN= 16 | 17 | :CheckOpts 18 | if "%1"=="" goto :CheckOptsDone 19 | if /I "%1"=="-?" goto :Usage 20 | if /I "%1"=="--install-cygwin" (set INSTALL_CYGWIN=1) & shift & goto :CheckOpts 21 | goto :Usage 22 | 23 | :CheckOptsDone 24 | 25 | if "%INSTALL_CYGWIN%"=="1" call :InstallCygwin 26 | 27 | REM Set build variables 28 | 29 | set BUILD=i686-pc-cygwin 30 | set HOST=aarch64-w64-cygwin 31 | if NOT DEFINED SH if exist c:\cygwin\bin\sh.exe set SH=c:\cygwin\bin\sh.exe 32 | 33 | REM Initialise ARM64 build environment 34 | 35 | if NOT DEFINED VCVARSALL ( 36 | for /F "tokens=*" %%i in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64') DO @(set VCVARSALL="%%i\VC\Auxiliary\Build\vcvarsall.bat") 37 | ) 38 | if ^%VCVARSALL:~0,1% NEQ ^" SET VCVARSALL="%VCVARSALL%" 39 | call %VCVARSALL% x86_arm64 40 | pushd %LIBFFI_SOURCE% 41 | %SH% --login -lc "cygcheck -dc cygwin" 42 | set GET_MSVCC=%SH% -lc "cd $LIBFFI_SOURCE; export MSVCC=`/usr/bin/find $PWD -name msvcc.sh`; echo ${MSVCC};" 43 | FOR /F "usebackq delims==" %%i IN (`%GET_MSVCC%`) do @set MSVCC=%%i 44 | set MSVCC=%MSVCC% -marm64 45 | 46 | echo Configuring and building libffi for ARM64 47 | 48 | %SH% -lc "(cd $LIBFFI_SOURCE; ./autogen.sh)" 49 | %SH% -lc "(cd $LIBFFI_SOURCE; ./configure CC='%MSVCC%' CXX='%MSVCC%' LD='link' CPP='cl -nologo -EP' CXXCPP='cl -nologo -EP' CPPFLAGS='-DFFI_BUILDING_DLL' NM='dumpbin -symbols' STRIP=':' --build=$BUILD --host=$HOST --enable-static --disable-shared)" 50 | %SH% -lc "(cd $LIBFFI_SOURCE; cp src/aarch64/ffitarget.h include)" 51 | %SH% -lc "(cd $LIBFFI_SOURCE; make)" 52 | 53 | set LIBFFI_OUT=%~dp0 54 | 55 | echo copying files to %LIBFFI_OUT% 56 | if not exist %LIBFFI_OUT%\include (md %LIBFFI_OUT%\include) 57 | copy %LIBFFI_SOURCE%\%HOST%\.libs\libffi.lib %LIBFFI_OUT%\ffi.lib || exit /B 1 58 | copy %LIBFFI_SOURCE%\%HOST%\fficonfig.h %LIBFFI_OUT%\include || exit /B 1 59 | copy %LIBFFI_SOURCE%\%HOST%\include\*.h %LIBFFI_OUT%\include || exit /B 1 60 | popd 61 | exit /B 62 | 63 | :InstallCygwin 64 | setlocal 65 | set CYG_ROOT=C:/cygwin 66 | set CYG_CACHE=C:/cygwin/var/cache/setup 67 | set CYG_MIRROR=http://mirrors.kernel.org/sourceware/cygwin/ 68 | powershell -c "Invoke-WebRequest https://cygwin.com/setup-x86.exe -OutFile setup.exe" 69 | setup.exe -qgnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P dejagnu -P autoconf -P automake -P libtool -P make 70 | endlocal 71 | exit /B 72 | -------------------------------------------------------------------------------- /src/c/libffi_arm64/ffi.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/src/c/libffi_arm64/ffi.lib -------------------------------------------------------------------------------- /src/c/libffi_arm64/include/ffitarget.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2009, 2010, 2011, 2012 ARM Ltd. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | ``Software''), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 21 | 22 | #ifndef LIBFFI_TARGET_H 23 | #define LIBFFI_TARGET_H 24 | 25 | #ifndef LIBFFI_H 26 | #error "Please do not include ffitarget.h directly into your source. Use ffi.h instead." 27 | #endif 28 | 29 | #ifndef LIBFFI_ASM 30 | #ifdef __ILP32__ 31 | #define FFI_SIZEOF_ARG 8 32 | #define FFI_SIZEOF_JAVA_RAW 4 33 | typedef unsigned long long ffi_arg; 34 | typedef signed long long ffi_sarg; 35 | #elif defined(_WIN32) 36 | #define FFI_SIZEOF_ARG 8 37 | typedef unsigned long long ffi_arg; 38 | typedef signed long long ffi_sarg; 39 | #else 40 | typedef unsigned long ffi_arg; 41 | typedef signed long ffi_sarg; 42 | #endif 43 | 44 | typedef enum ffi_abi 45 | { 46 | FFI_FIRST_ABI = 0, 47 | FFI_SYSV, 48 | FFI_WIN64, 49 | FFI_LAST_ABI, 50 | #if defined(_WIN32) 51 | FFI_DEFAULT_ABI = FFI_WIN64 52 | #else 53 | FFI_DEFAULT_ABI = FFI_SYSV 54 | #endif 55 | } ffi_abi; 56 | #endif 57 | 58 | /* ---- Definitions for closures ----------------------------------------- */ 59 | 60 | #define FFI_CLOSURES 1 61 | #define FFI_NATIVE_RAW_API 0 62 | 63 | #if defined (FFI_EXEC_TRAMPOLINE_TABLE) && FFI_EXEC_TRAMPOLINE_TABLE 64 | 65 | #ifdef __MACH__ 66 | #define FFI_TRAMPOLINE_SIZE 16 67 | #define FFI_TRAMPOLINE_CLOSURE_OFFSET 16 68 | #else 69 | #error "No trampoline table implementation" 70 | #endif 71 | 72 | #else 73 | #define FFI_TRAMPOLINE_SIZE 24 74 | #define FFI_TRAMPOLINE_CLOSURE_OFFSET FFI_TRAMPOLINE_SIZE 75 | #endif 76 | 77 | #ifdef _WIN32 78 | #define FFI_EXTRA_CIF_FIELDS unsigned is_variadic 79 | #endif 80 | #define FFI_TARGET_SPECIFIC_VARIADIC 81 | 82 | /* ---- Internal ---- */ 83 | 84 | #if defined (__APPLE__) 85 | #define FFI_EXTRA_CIF_FIELDS unsigned aarch64_nfixedargs 86 | #elif !defined(_WIN32) 87 | /* iOS and Windows reserve x18 for the system. Disable Go closures until 88 | a new static chain is chosen. */ 89 | #define FFI_GO_CLOSURES 1 90 | #endif 91 | 92 | #ifndef _WIN32 93 | /* No complex type on Windows */ 94 | #define FFI_TARGET_HAS_COMPLEX_TYPE 95 | #endif 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/LICENSE: -------------------------------------------------------------------------------- 1 | libffi - Copyright (c) 1996-2003 Red Hat, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | ``Software''), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/README.ctypes: -------------------------------------------------------------------------------- 1 | The purpose is to hack the libffi sources so that they can be compiled 2 | with MSVC, and to extend them so that they have the features I need 3 | for ctypes. 4 | 5 | I retrieved the libffi sources from the gcc cvs repository on 6 | 2004-01-27. Then I did 'configure' in a 'build' subdirectory on a x86 7 | linux system, and copied the files I found useful. 8 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/ffi_common.h: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | ffi_common.h - Copyright (c) 1996 Red Hat, Inc. 3 | 4 | Common internal definitions and macros. Only necessary for building 5 | libffi. 6 | ----------------------------------------------------------------------- */ 7 | 8 | #ifndef FFI_COMMON_H 9 | #define FFI_COMMON_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | /* Check for the existence of memcpy. */ 19 | #if STDC_HEADERS 20 | # include 21 | #else 22 | # ifndef HAVE_MEMCPY 23 | # define memcpy(d, s, n) bcopy ((s), (d), (n)) 24 | # endif 25 | #endif 26 | 27 | #if defined(FFI_DEBUG) 28 | #include 29 | #endif 30 | 31 | #ifdef FFI_DEBUG 32 | /*@exits@*/ void ffi_assert(/*@temp@*/ char *expr, /*@temp@*/ char *file, int line); 33 | void ffi_stop_here(void); 34 | void ffi_type_test(/*@temp@*/ /*@out@*/ ffi_type *a, /*@temp@*/ char *file, int line); 35 | 36 | #define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__)) 37 | #define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) 38 | #define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__) 39 | #else 40 | #define FFI_ASSERT(x) 41 | #define FFI_ASSERT_AT(x, f, l) 42 | #define FFI_ASSERT_VALID_TYPE(x) 43 | #endif 44 | 45 | #define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1) 46 | 47 | /* Perform machine dependent cif processing */ 48 | ffi_status ffi_prep_cif_machdep(ffi_cif *cif); 49 | 50 | /* Extended cif, used in callback from assembly routine */ 51 | typedef struct 52 | { 53 | /*@dependent@*/ ffi_cif *cif; 54 | /*@dependent@*/ void *rvalue; 55 | /*@dependent@*/ void **avalue; 56 | } extended_cif; 57 | 58 | /* Terse sized type definitions. */ 59 | typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); 60 | typedef signed int SINT8 __attribute__((__mode__(__QI__))); 61 | typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); 62 | typedef signed int SINT16 __attribute__((__mode__(__HI__))); 63 | typedef unsigned int UINT32 __attribute__((__mode__(__SI__))); 64 | typedef signed int SINT32 __attribute__((__mode__(__SI__))); 65 | typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); 66 | typedef signed int SINT64 __attribute__((__mode__(__DI__))); 67 | 68 | typedef float FLOAT32; 69 | 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/fficonfig.h: -------------------------------------------------------------------------------- 1 | /* fficonfig.h. Originally created by configure, now hand_maintained for MSVC. */ 2 | 3 | /* fficonfig.h. Generated automatically by configure. */ 4 | /* fficonfig.h.in. Generated automatically from configure.in by autoheader. */ 5 | 6 | /* Define this for MSVC, but not for mingw32! */ 7 | #ifdef _MSC_VER 8 | #define __attribute__(x) /* */ 9 | #endif 10 | #define alloca _alloca 11 | 12 | /*----------------------------------------------------------------*/ 13 | 14 | /* Define if using alloca.c. */ 15 | /* #undef C_ALLOCA */ 16 | 17 | /* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. 18 | This function is required for alloca.c support on those systems. */ 19 | /* #undef CRAY_STACKSEG_END */ 20 | 21 | /* Define if you have alloca, as a function or macro. */ 22 | #define HAVE_ALLOCA 1 23 | 24 | /* Define if you have and it should be used (not on Ultrix). */ 25 | /* #define HAVE_ALLOCA_H 1 */ 26 | 27 | /* If using the C implementation of alloca, define if you know the 28 | direction of stack growth for your system; otherwise it will be 29 | automatically deduced at run-time. 30 | STACK_DIRECTION > 0 => grows toward higher addresses 31 | STACK_DIRECTION < 0 => grows toward lower addresses 32 | STACK_DIRECTION = 0 => direction of growth unknown 33 | */ 34 | /* #undef STACK_DIRECTION */ 35 | 36 | /* Define if you have the ANSI C header files. */ 37 | #define STDC_HEADERS 1 38 | 39 | /* Define if you have the memcpy function. */ 40 | #define HAVE_MEMCPY 1 41 | 42 | /* Define if read-only mmap of a plain file works. */ 43 | //#define HAVE_MMAP_FILE 1 44 | 45 | /* Define if mmap of /dev/zero works. */ 46 | //#define HAVE_MMAP_DEV_ZERO 1 47 | 48 | /* Define if mmap with MAP_ANON(YMOUS) works. */ 49 | //#define HAVE_MMAP_ANON 1 50 | 51 | /* The number of bytes in type double */ 52 | #define SIZEOF_DOUBLE 8 53 | 54 | /* The number of bytes in type long double */ 55 | #define SIZEOF_LONG_DOUBLE 12 56 | 57 | /* Define if you have the long double type and it is bigger than a double */ 58 | #define HAVE_LONG_DOUBLE 1 59 | 60 | /* whether byteorder is bigendian */ 61 | /* #undef WORDS_BIGENDIAN */ 62 | 63 | /* Define if the host machine stores words of multi-word integers in 64 | big-endian order. */ 65 | /* #undef HOST_WORDS_BIG_ENDIAN */ 66 | 67 | /* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ 68 | #define BYTEORDER 1234 69 | 70 | /* Define if your assembler and linker support unaligned PC relative relocs. */ 71 | /* #undef HAVE_AS_SPARC_UA_PCREL */ 72 | 73 | /* Define if your assembler supports .register. */ 74 | /* #undef HAVE_AS_REGISTER_PSEUDO_OP */ 75 | 76 | /* Define if .eh_frame sections should be read-only. */ 77 | /* #undef HAVE_RO_EH_FRAME */ 78 | 79 | /* Define to the flags needed for the .section .eh_frame directive. */ 80 | /* #define EH_FRAME_FLAGS "aw" */ 81 | 82 | /* Define to the flags needed for the .section .eh_frame directive. */ 83 | /* #define EH_FRAME_FLAGS "aw" */ 84 | 85 | /* Define this if you want extra debugging. */ 86 | /* #undef FFI_DEBUG */ 87 | 88 | /* Define this is you do not want support for aggregate types. */ 89 | /* #undef FFI_NO_STRUCTS */ 90 | 91 | /* Define this is you do not want support for the raw API. */ 92 | /* #undef FFI_NO_RAW_API */ 93 | 94 | /* Define this if you are using Purify and want to suppress spurious messages. */ 95 | /* #undef USING_PURIFY */ 96 | 97 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/ffitarget.h: -------------------------------------------------------------------------------- 1 | /* -----------------------------------------------------------------*-C-*- 2 | ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. 3 | Target configuration macros for x86 and x86-64. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | ``Software''), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | ----------------------------------------------------------------------- */ 25 | 26 | #ifndef LIBFFI_TARGET_H 27 | #define LIBFFI_TARGET_H 28 | 29 | /* ---- System specific configurations ----------------------------------- */ 30 | 31 | #if defined (X86_64) && defined (__i386__) 32 | #undef X86_64 33 | #define X86 34 | #endif 35 | 36 | /* ---- Generic type definitions ----------------------------------------- */ 37 | 38 | #ifndef LIBFFI_ASM 39 | #ifndef _WIN64 40 | typedef unsigned long ffi_arg; 41 | #else 42 | typedef unsigned __int64 ffi_arg; 43 | #endif 44 | typedef signed long ffi_sarg; 45 | 46 | typedef enum ffi_abi { 47 | FFI_FIRST_ABI = 0, 48 | 49 | /* ---- Intel x86 Win32 ---------- */ 50 | FFI_SYSV, 51 | #ifndef _WIN64 52 | FFI_STDCALL, 53 | #endif 54 | /* TODO: Add fastcall support for the sake of completeness */ 55 | FFI_DEFAULT_ABI = FFI_SYSV, 56 | 57 | /* ---- Intel x86 and AMD x86-64 - */ 58 | /* #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) */ 59 | /* FFI_SYSV, */ 60 | /* FFI_UNIX64,*/ /* Unix variants all use the same ABI for x86-64 */ 61 | /* #ifdef __i386__ */ 62 | /* FFI_DEFAULT_ABI = FFI_SYSV, */ 63 | /* #else */ 64 | /* FFI_DEFAULT_ABI = FFI_UNIX64, */ 65 | /* #endif */ 66 | /* #endif */ 67 | 68 | FFI_LAST_ABI = FFI_DEFAULT_ABI + 1 69 | } ffi_abi; 70 | #endif 71 | 72 | /* ---- Definitions for closures ----------------------------------------- */ 73 | 74 | #define FFI_CLOSURES 1 75 | 76 | #ifdef _WIN64 77 | #define FFI_TRAMPOLINE_SIZE 29 78 | #define FFI_NATIVE_RAW_API 0 79 | #else 80 | #define FFI_TRAMPOLINE_SIZE 15 81 | #define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ 82 | #endif 83 | 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/types.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | types.c - Copyright (c) 1996, 1998 Red Hat, Inc. 3 | 4 | Predefined ffi_types needed by libffi. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | ``Software''), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | ----------------------------------------------------------------------- */ 25 | 26 | #include 27 | #include 28 | 29 | /* Type definitions */ 30 | 31 | #define FFI_INTEGRAL_TYPEDEF(n, s, a, t) ffi_type ffi_type_##n = { s, a, t, NULL } 32 | #define FFI_AGGREGATE_TYPEDEF(n, e) ffi_type ffi_type_##n = { 0, 0, FFI_TYPE_STRUCT, e } 33 | 34 | /* Size and alignment are fake here. They must not be 0. */ 35 | FFI_INTEGRAL_TYPEDEF(void, 1, 1, FFI_TYPE_VOID); 36 | 37 | FFI_INTEGRAL_TYPEDEF(uint8, 1, 1, FFI_TYPE_UINT8); 38 | FFI_INTEGRAL_TYPEDEF(sint8, 1, 1, FFI_TYPE_SINT8); 39 | FFI_INTEGRAL_TYPEDEF(uint16, 2, 2, FFI_TYPE_UINT16); 40 | FFI_INTEGRAL_TYPEDEF(sint16, 2, 2, FFI_TYPE_SINT16); 41 | FFI_INTEGRAL_TYPEDEF(uint32, 4, 4, FFI_TYPE_UINT32); 42 | FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32); 43 | FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT); 44 | 45 | #if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \ 46 | || defined IA64 || defined _WIN64 47 | 48 | FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER); 49 | 50 | #else 51 | 52 | FFI_INTEGRAL_TYPEDEF(pointer, 4, 4, FFI_TYPE_POINTER); 53 | 54 | #endif 55 | 56 | #if defined X86 || defined X86_WIN32 || defined ARM || defined M68K 57 | 58 | FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); 59 | FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); 60 | 61 | #elif defined SH 62 | 63 | FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); 64 | FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); 65 | 66 | #else 67 | 68 | FFI_INTEGRAL_TYPEDEF(uint64, 8, 8, FFI_TYPE_UINT64); 69 | FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64); 70 | 71 | #endif 72 | 73 | 74 | #if defined X86 || defined X86_WIN32 || defined M68K 75 | 76 | FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); 77 | FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE); 78 | 79 | #elif defined ARM || defined SH || defined POWERPC_AIX || defined POWERPC_DARWIN 80 | 81 | FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); 82 | FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE); 83 | 84 | #elif defined SPARC 85 | 86 | FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); 87 | #ifdef SPARC64 88 | FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); 89 | #else 90 | FFI_INTEGRAL_TYPEDEF(longdouble, 16, 8, FFI_TYPE_LONGDOUBLE); 91 | #endif 92 | 93 | #elif defined X86_64 94 | 95 | FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); 96 | FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); 97 | 98 | #else 99 | 100 | FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); 101 | FFI_INTEGRAL_TYPEDEF(longdouble, 8, 8, FFI_TYPE_LONGDOUBLE); 102 | 103 | #endif 104 | 105 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/win32.c: -------------------------------------------------------------------------------- 1 | /* ----------------------------------------------------------------------- 2 | win32.S - Copyright (c) 1996, 1998, 2001, 2002 Red Hat, Inc. 3 | Copyright (c) 2001 John Beniton 4 | Copyright (c) 2002 Ranjit Mathew 5 | 6 | 7 | X86 Foreign Function Interface 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | ``Software''), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included 18 | in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | ----------------------------------------------------------------------- */ 28 | 29 | /* theller: almost verbatim translation from gas syntax to MSVC inline 30 | assembler code. */ 31 | 32 | /* theller: ffi_call_x86 now returns an integer - the difference of the stack 33 | pointer before and after the function call. If everything is ok, zero is 34 | returned. If stdcall functions are passed the wrong number of arguments, 35 | the difference will be nonzero. */ 36 | 37 | #include 38 | #include 39 | 40 | __declspec(naked) int 41 | ffi_call_x86(void (* prepfunc)(char *, extended_cif *), /* 8 */ 42 | extended_cif *ecif, /* 12 */ 43 | unsigned bytes, /* 16 */ 44 | unsigned flags, /* 20 */ 45 | unsigned *rvalue, /* 24 */ 46 | void (*fn)()) /* 28 */ 47 | { 48 | _asm { 49 | push ebp 50 | mov ebp, esp 51 | 52 | push esi // NEW: this register must be preserved across function calls 53 | // XXX SAVE ESP NOW! 54 | mov esi, esp // save stack pointer before the call 55 | 56 | // Make room for all of the new args. 57 | mov ecx, [ebp+16] 58 | sub esp, ecx // sub esp, bytes 59 | 60 | mov eax, esp 61 | 62 | // Place all of the ffi_prep_args in position 63 | push [ebp + 12] // ecif 64 | push eax 65 | call [ebp + 8] // prepfunc 66 | 67 | // Return stack to previous state and call the function 68 | add esp, 8 69 | // FIXME: Align the stack to a 128-bit boundary to avoid 70 | // potential performance hits. 71 | call [ebp + 28] 72 | 73 | // Load ecif->cif->abi 74 | mov ecx, [ebp + 12] 75 | mov ecx, [ecx]ecif.cif 76 | mov ecx, [ecx]ecif.cif.abi 77 | 78 | cmp ecx, FFI_STDCALL 79 | je noclean 80 | // STDCALL: Remove the space we pushed for the args 81 | mov ecx, [ebp + 16] 82 | add esp, ecx 83 | // CDECL: Caller has already cleaned the stack 84 | noclean: 85 | // Check that esp has the same value as before! 86 | sub esi, esp 87 | 88 | // Load %ecx with the return type code 89 | mov ecx, [ebp + 20] 90 | 91 | // If the return value pointer is NULL, assume no return value. 92 | /* 93 | Intel asm is weird. We have to explicitly specify 'DWORD PTR' in the nexr instruction, 94 | otherwise only one BYTE will be compared (instead of a DWORD)! 95 | */ 96 | cmp DWORD PTR [ebp + 24], 0 97 | jne sc_retint 98 | 99 | // Even if there is no space for the return value, we are 100 | // obliged to handle floating-point values. 101 | cmp ecx, FFI_TYPE_FLOAT 102 | jne sc_noretval 103 | // fstp %st(0) 104 | fstp st(0) 105 | 106 | jmp sc_epilogue 107 | 108 | sc_retint: 109 | cmp ecx, FFI_TYPE_INT 110 | jne sc_retfloat 111 | // # Load %ecx with the pointer to storage for the return value 112 | mov ecx, [ebp + 24] 113 | mov [ecx + 0], eax 114 | jmp sc_epilogue 115 | 116 | sc_retfloat: 117 | cmp ecx, FFI_TYPE_FLOAT 118 | jne sc_retdouble 119 | // Load %ecx with the pointer to storage for the return value 120 | mov ecx, [ebp+24] 121 | // fstps (%ecx) 122 | fstp DWORD PTR [ecx] 123 | jmp sc_epilogue 124 | 125 | sc_retdouble: 126 | cmp ecx, FFI_TYPE_DOUBLE 127 | jne sc_retlongdouble 128 | // movl 24(%ebp),%ecx 129 | mov ecx, [ebp+24] 130 | fstp QWORD PTR [ecx] 131 | jmp sc_epilogue 132 | 133 | jmp sc_retlongdouble // avoid warning about unused label 134 | sc_retlongdouble: 135 | cmp ecx, FFI_TYPE_LONGDOUBLE 136 | jne sc_retint64 137 | // Load %ecx with the pointer to storage for the return value 138 | mov ecx, [ebp+24] 139 | // fstpt (%ecx) 140 | fstp QWORD PTR [ecx] /* XXX ??? */ 141 | jmp sc_epilogue 142 | 143 | sc_retint64: 144 | cmp ecx, FFI_TYPE_SINT64 145 | jne sc_retstruct 146 | // Load %ecx with the pointer to storage for the return value 147 | mov ecx, [ebp+24] 148 | mov [ecx+0], eax 149 | mov [ecx+4], edx 150 | 151 | sc_retstruct: 152 | // Nothing to do! 153 | 154 | sc_noretval: 155 | sc_epilogue: 156 | mov eax, esi 157 | pop esi // NEW restore: must be preserved across function calls 158 | mov esp, ebp 159 | pop ebp 160 | ret 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/win64.asm: -------------------------------------------------------------------------------- 1 | PUBLIC ffi_call_AMD64 2 | 3 | EXTRN __chkstk:NEAR 4 | EXTRN ffi_closure_SYSV:NEAR 5 | 6 | _TEXT SEGMENT 7 | 8 | ;;; ffi_closure_OUTER will be called with these registers set: 9 | ;;; rax points to 'closure' 10 | ;;; r11 contains a bit mask that specifies which of the 11 | ;;; first four parameters are float or double 12 | ;;; 13 | ;;; It must move the parameters passed in registers to their stack location, 14 | ;;; call ffi_closure_SYSV for the actual work, then return the result. 15 | ;;; 16 | ffi_closure_OUTER PROC FRAME 17 | ;; save actual arguments to their stack space. 18 | test r11, 1 19 | jne first_is_float 20 | mov QWORD PTR [rsp+8], rcx 21 | jmp second 22 | first_is_float: 23 | movlpd QWORD PTR [rsp+8], xmm0 24 | 25 | second: 26 | test r11, 2 27 | jne second_is_float 28 | mov QWORD PTR [rsp+16], rdx 29 | jmp third 30 | second_is_float: 31 | movlpd QWORD PTR [rsp+16], xmm1 32 | 33 | third: 34 | test r11, 4 35 | jne third_is_float 36 | mov QWORD PTR [rsp+24], r8 37 | jmp forth 38 | third_is_float: 39 | movlpd QWORD PTR [rsp+24], xmm2 40 | 41 | forth: 42 | test r11, 8 43 | jne forth_is_float 44 | mov QWORD PTR [rsp+32], r9 45 | jmp done 46 | forth_is_float: 47 | movlpd QWORD PTR [rsp+32], xmm3 48 | 49 | done: 50 | .ALLOCSTACK 40 51 | sub rsp, 40 52 | .ENDPROLOG 53 | mov rcx, rax ; context is first parameter 54 | mov rdx, rsp ; stack is second parameter 55 | add rdx, 40 ; correct our own area 56 | mov rax, ffi_closure_SYSV 57 | call rax ; call the real closure function 58 | ;; Here, code is missing that handles float return values 59 | add rsp, 40 60 | movd xmm0, rax ; In case the closure returned a float. 61 | ret 0 62 | ffi_closure_OUTER ENDP 63 | 64 | 65 | ;;; ffi_call_AMD64 66 | 67 | stack$ = 0 68 | prepfunc$ = 32 69 | ecif$ = 40 70 | bytes$ = 48 71 | flags$ = 56 72 | rvalue$ = 64 73 | fn$ = 72 74 | 75 | ffi_call_AMD64 PROC FRAME 76 | 77 | mov QWORD PTR [rsp+32], r9 78 | mov QWORD PTR [rsp+24], r8 79 | mov QWORD PTR [rsp+16], rdx 80 | mov QWORD PTR [rsp+8], rcx 81 | .PUSHREG rbp 82 | push rbp 83 | .ALLOCSTACK 48 84 | sub rsp, 48 ; 00000030H 85 | .SETFRAME rbp, 32 86 | lea rbp, QWORD PTR [rsp+32] 87 | .ENDPROLOG 88 | 89 | mov eax, DWORD PTR bytes$[rbp] 90 | add rax, 15 91 | and rax, -16 92 | call __chkstk 93 | sub rsp, rax 94 | lea rax, QWORD PTR [rsp+32] 95 | mov QWORD PTR stack$[rbp], rax 96 | 97 | mov rdx, QWORD PTR ecif$[rbp] 98 | mov rcx, QWORD PTR stack$[rbp] 99 | call QWORD PTR prepfunc$[rbp] 100 | 101 | mov rsp, QWORD PTR stack$[rbp] 102 | 103 | movlpd xmm3, QWORD PTR [rsp+24] 104 | movd r9, xmm3 105 | 106 | movlpd xmm2, QWORD PTR [rsp+16] 107 | movd r8, xmm2 108 | 109 | movlpd xmm1, QWORD PTR [rsp+8] 110 | movd rdx, xmm1 111 | 112 | movlpd xmm0, QWORD PTR [rsp] 113 | movd rcx, xmm0 114 | 115 | call QWORD PTR fn$[rbp] 116 | ret_int$: 117 | cmp DWORD PTR flags$[rbp], 1 ; FFI_TYPE_INT 118 | jne ret_float$ 119 | 120 | mov rcx, QWORD PTR rvalue$[rbp] 121 | mov DWORD PTR [rcx], eax 122 | jmp SHORT ret_nothing$ 123 | 124 | ret_float$: 125 | cmp DWORD PTR flags$[rbp], 2 ; FFI_TYPE_FLOAT 126 | jne SHORT ret_double$ 127 | 128 | mov rax, QWORD PTR rvalue$[rbp] 129 | movlpd QWORD PTR [rax], xmm0 130 | jmp SHORT ret_nothing$ 131 | 132 | ret_double$: 133 | cmp DWORD PTR flags$[rbp], 3 ; FFI_TYPE_DOUBLE 134 | jne SHORT ret_int64$ 135 | 136 | mov rax, QWORD PTR rvalue$[rbp] 137 | movlpd QWORD PTR [rax], xmm0 138 | jmp SHORT ret_nothing$ 139 | 140 | ret_int64$: 141 | cmp DWORD PTR flags$[rbp], 12 ; FFI_TYPE_SINT64 142 | jne ret_nothing$ 143 | 144 | mov rcx, QWORD PTR rvalue$[rbp] 145 | mov QWORD PTR [rcx], rax 146 | jmp SHORT ret_nothing$ 147 | 148 | ret_nothing$: 149 | xor eax, eax 150 | 151 | lea rsp, QWORD PTR [rbp+16] 152 | pop rbp 153 | ret 0 154 | ffi_call_AMD64 ENDP 155 | _TEXT ENDS 156 | END 157 | -------------------------------------------------------------------------------- /src/c/libffi_x86_x64/win64.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/src/c/libffi_x86_x64/win64.obj -------------------------------------------------------------------------------- /src/c/malloc_closure.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is from CPython's Modules/_ctypes/malloc_closure.c 3 | * and has received some edits. 4 | */ 5 | 6 | #include 7 | #ifdef MS_WIN32 8 | #include 9 | #else 10 | #include 11 | #include 12 | # if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) 13 | # define MAP_ANONYMOUS MAP_ANON 14 | # endif 15 | #endif 16 | 17 | /* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. 18 | 19 | This is, apparently, an undocumented change to ffi_prep_closure(): 20 | depending on the Linux kernel we're running on, we must give it a 21 | mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only 22 | PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a 23 | mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!), 24 | but in that situation libffi is fine with only PROT_READ|PROT_WRITE. 25 | There is nothing in the libffi API to know that, though, so we have 26 | to guess by parsing /proc/self/status. "Meh." 27 | */ 28 | #ifdef __linux__ 29 | #include 30 | 31 | static int emutramp_enabled = -1; 32 | 33 | static int 34 | emutramp_enabled_check (void) 35 | { 36 | char *buf = NULL; 37 | size_t len = 0; 38 | FILE *f; 39 | int ret; 40 | f = fopen ("/proc/self/status", "r"); 41 | if (f == NULL) 42 | return 0; 43 | ret = 0; 44 | 45 | while (getline (&buf, &len, f) != -1) 46 | if (!strncmp (buf, "PaX:", 4)) 47 | { 48 | char emutramp; 49 | if (sscanf (buf, "%*s %*c%c", &emutramp) == 1) 50 | ret = (emutramp == 'E'); 51 | break; 52 | } 53 | free (buf); 54 | fclose (f); 55 | return ret; 56 | } 57 | 58 | #define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \ 59 | : (emutramp_enabled = emutramp_enabled_check ())) 60 | #else 61 | #define is_emutramp_enabled() 0 62 | #endif 63 | 64 | 65 | /* 'allocate_num_pages' is dynamically adjusted starting from one 66 | page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is 67 | meant to handle both the common case of not needing a lot of pages, 68 | and the rare case of needing many of them. Systems in general have a 69 | limit of how many mmap'd blocks can be open. 70 | */ 71 | 72 | #define PAGE_ALLOCATION_GROWTH_RATE 1.3 73 | 74 | static Py_ssize_t allocate_num_pages = 0; 75 | 76 | /* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */ 77 | 78 | /******************************************************************/ 79 | 80 | union mmaped_block { 81 | ffi_closure closure; 82 | union mmaped_block *next; 83 | }; 84 | 85 | static union mmaped_block *free_list = 0; 86 | static Py_ssize_t _pagesize = 0; 87 | 88 | static void more_core(void) 89 | { 90 | union mmaped_block *item; 91 | Py_ssize_t count, i; 92 | 93 | /* determine the pagesize */ 94 | #ifdef MS_WIN32 95 | if (!_pagesize) { 96 | SYSTEM_INFO systeminfo; 97 | GetSystemInfo(&systeminfo); 98 | _pagesize = systeminfo.dwPageSize; 99 | } 100 | #else 101 | if (!_pagesize) { 102 | #ifdef _SC_PAGESIZE 103 | _pagesize = sysconf(_SC_PAGESIZE); 104 | #else 105 | _pagesize = getpagesize(); 106 | #endif 107 | } 108 | #endif 109 | if (_pagesize <= 0) 110 | _pagesize = 4096; 111 | 112 | /* bump 'allocate_num_pages' */ 113 | allocate_num_pages = 1 + ( 114 | (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE)); 115 | 116 | /* calculate the number of mmaped_blocks to allocate */ 117 | count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block); 118 | 119 | /* allocate a memory block */ 120 | #ifdef MS_WIN32 121 | item = (union mmaped_block *)VirtualAlloc(NULL, 122 | count * sizeof(union mmaped_block), 123 | MEM_COMMIT, 124 | PAGE_EXECUTE_READWRITE); 125 | if (item == NULL) 126 | return; 127 | #else 128 | { 129 | int prot = PROT_READ | PROT_WRITE | PROT_EXEC; 130 | if (is_emutramp_enabled ()) 131 | prot &= ~PROT_EXEC; 132 | item = (union mmaped_block *)mmap(NULL, 133 | allocate_num_pages * _pagesize, 134 | prot, 135 | MAP_PRIVATE | MAP_ANONYMOUS, 136 | -1, 137 | 0); 138 | if (item == (void *)MAP_FAILED) 139 | return; 140 | } 141 | #endif 142 | 143 | #ifdef MALLOC_CLOSURE_DEBUG 144 | printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n", 145 | item, (long)(allocate_num_pages * _pagesize), (long)count); 146 | #endif 147 | /* put them into the free list */ 148 | for (i = 0; i < count; ++i) { 149 | item->next = free_list; 150 | free_list = item; 151 | ++item; 152 | } 153 | } 154 | 155 | /******************************************************************/ 156 | 157 | /* put the item back into the free list */ 158 | static void cffi_closure_free(ffi_closure *p) 159 | { 160 | union mmaped_block *item = (union mmaped_block *)p; 161 | item->next = free_list; 162 | free_list = item; 163 | } 164 | 165 | /* return one item from the free list, allocating more if needed */ 166 | static ffi_closure *cffi_closure_alloc(void) 167 | { 168 | union mmaped_block *item; 169 | if (!free_list) 170 | more_core(); 171 | if (!free_list) 172 | return NULL; 173 | item = free_list; 174 | free_list = item->next; 175 | return &item->closure; 176 | } 177 | -------------------------------------------------------------------------------- /src/c/misc_thread_posix.h: -------------------------------------------------------------------------------- 1 | /* 2 | Logic for a better replacement of PyGILState_Ensure(). 3 | 4 | This version is ready to handle the case of a non-Python-started 5 | thread in which we do a large number of calls to CFFI callbacks. If 6 | we were to rely on PyGILState_Ensure() for that, we would constantly 7 | be creating and destroying PyThreadStates---it is slow, and 8 | PyThreadState_Delete() will actually walk the list of all thread 9 | states, making it O(n). :-( 10 | 11 | This version only creates one PyThreadState object the first time we 12 | see a given thread, and keep it alive until the thread is really 13 | shut down, using a destructor on the tls key. 14 | */ 15 | 16 | #include 17 | #include "misc_thread_common.h" 18 | 19 | 20 | static pthread_key_t cffi_tls_key; 21 | 22 | static void init_cffi_tls(void) 23 | { 24 | if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0) 25 | PyErr_SetString(PyExc_OSError, "pthread_key_create() failed"); 26 | } 27 | 28 | static struct cffi_tls_s *_make_cffi_tls(void) 29 | { 30 | void *p = calloc(1, sizeof(struct cffi_tls_s)); 31 | if (p == NULL) 32 | return NULL; 33 | if (pthread_setspecific(cffi_tls_key, p) != 0) { 34 | free(p); 35 | return NULL; 36 | } 37 | return p; 38 | } 39 | 40 | static struct cffi_tls_s *get_cffi_tls(void) 41 | { 42 | void *p = pthread_getspecific(cffi_tls_key); 43 | if (p == NULL) 44 | p = _make_cffi_tls(); 45 | return (struct cffi_tls_s *)p; 46 | } 47 | 48 | #define save_errno save_errno_only 49 | #define restore_errno restore_errno_only 50 | -------------------------------------------------------------------------------- /src/c/wchar_helper_3.h: -------------------------------------------------------------------------------- 1 | /* 2 | * wchar_t helpers, version CPython >= 3.3. 3 | * 4 | * CPython 3.3 added support for sys.maxunicode == 0x10FFFF on all 5 | * platforms, even ones with wchar_t limited to 2 bytes. As such, 6 | * this code here works from the outside like wchar_helper.h in the 7 | * case Py_UNICODE_SIZE == 4, but the implementation is very different. 8 | */ 9 | 10 | typedef uint16_t cffi_char16_t; 11 | typedef uint32_t cffi_char32_t; 12 | 13 | 14 | static PyObject * 15 | _my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) 16 | { 17 | return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, w, size); 18 | } 19 | 20 | static PyObject * 21 | _my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) 22 | { 23 | /* are there any surrogate pairs, and if so, how many? */ 24 | Py_ssize_t i, count_surrogates = 0; 25 | for (i = 0; i < size - 1; i++) { 26 | if (0xD800 <= w[i] && w[i] <= 0xDBFF && 27 | 0xDC00 <= w[i+1] && w[i+1] <= 0xDFFF) 28 | count_surrogates++; 29 | } 30 | if (count_surrogates == 0) { 31 | /* no, fast path */ 32 | return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, w, size); 33 | } 34 | else 35 | { 36 | PyObject *result = PyUnicode_New(size - count_surrogates, 0x10FFFF); 37 | Py_UCS4 *data; 38 | assert(PyUnicode_KIND(result) == PyUnicode_4BYTE_KIND); 39 | data = PyUnicode_4BYTE_DATA(result); 40 | 41 | for (i = 0; i < size; i++) 42 | { 43 | cffi_char32_t ch = w[i]; 44 | if (0xD800 <= ch && ch <= 0xDBFF && i < size - 1) { 45 | cffi_char32_t ch2 = w[i + 1]; 46 | if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { 47 | ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; 48 | i++; 49 | } 50 | } 51 | *data++ = ch; 52 | } 53 | return result; 54 | } 55 | } 56 | 57 | static int 58 | _my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, 59 | char *err_got) 60 | { 61 | cffi_char32_t ch; 62 | if (PyUnicode_GET_LENGTH(unicode) != 1) { 63 | sprintf(err_got, "unicode string of length %zd", 64 | PyUnicode_GET_LENGTH(unicode)); 65 | return -1; 66 | } 67 | ch = PyUnicode_READ_CHAR(unicode, 0); 68 | 69 | if (ch > 0xFFFF) 70 | { 71 | sprintf(err_got, "larger-than-0xFFFF character"); 72 | return -1; 73 | } 74 | *result = (cffi_char16_t)ch; 75 | return 0; 76 | } 77 | 78 | static int 79 | _my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, 80 | char *err_got) 81 | { 82 | if (PyUnicode_GET_LENGTH(unicode) != 1) { 83 | sprintf(err_got, "unicode string of length %zd", 84 | PyUnicode_GET_LENGTH(unicode)); 85 | return -1; 86 | } 87 | *result = PyUnicode_READ_CHAR(unicode, 0); 88 | return 0; 89 | } 90 | 91 | static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) 92 | { 93 | Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); 94 | Py_ssize_t result = length; 95 | unsigned int kind = PyUnicode_KIND(unicode); 96 | 97 | if (kind == PyUnicode_4BYTE_KIND) 98 | { 99 | Py_UCS4 *data = PyUnicode_4BYTE_DATA(unicode); 100 | Py_ssize_t i; 101 | for (i = 0; i < length; i++) { 102 | if (data[i] > 0xFFFF) 103 | result++; 104 | } 105 | } 106 | return result; 107 | } 108 | 109 | static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) 110 | { 111 | return PyUnicode_GET_LENGTH(unicode); 112 | } 113 | 114 | static int _my_PyUnicode_AsChar16(PyObject *unicode, 115 | cffi_char16_t *result, 116 | Py_ssize_t resultlen) 117 | { 118 | Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); 119 | unsigned int kind = PyUnicode_KIND(unicode); 120 | void *data = PyUnicode_DATA(unicode); 121 | Py_ssize_t i; 122 | 123 | for (i = 0; i < len; i++) { 124 | cffi_char32_t ordinal = PyUnicode_READ(kind, data, i); 125 | if (ordinal > 0xFFFF) { 126 | if (ordinal > 0x10FFFF) { 127 | PyErr_Format(PyExc_ValueError, 128 | "unicode character out of range for " 129 | "conversion to char16_t: 0x%x", (int)ordinal); 130 | return -1; 131 | } 132 | ordinal -= 0x10000; 133 | *result++ = 0xD800 | (ordinal >> 10); 134 | *result++ = 0xDC00 | (ordinal & 0x3FF); 135 | } 136 | else 137 | *result++ = ordinal; 138 | } 139 | return 0; 140 | } 141 | 142 | static int _my_PyUnicode_AsChar32(PyObject *unicode, 143 | cffi_char32_t *result, 144 | Py_ssize_t resultlen) 145 | { 146 | if (PyUnicode_AsUCS4(unicode, (Py_UCS4 *)result, resultlen, 0) == NULL) 147 | return -1; 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /src/cffi/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', 2 | 'FFIError'] 3 | 4 | from .api import FFI 5 | from .error import CDefError, FFIError, VerificationError, VerificationMissing 6 | from .error import PkgConfigError 7 | 8 | __version__ = "1.18.0.dev0" 9 | __version_info__ = (1, 18, 0, 'dev0') 10 | 11 | # The verifier module file names are based on the CRC32 of a string that 12 | # contains the following version number. It may be older than __version__ 13 | # if nothing is clearly incompatible. 14 | __version_verifier_modules__ = "0.8.6" 15 | -------------------------------------------------------------------------------- /src/cffi/_cffi_errors.h: -------------------------------------------------------------------------------- 1 | #ifndef CFFI_MESSAGEBOX 2 | # ifdef _MSC_VER 3 | # define CFFI_MESSAGEBOX 1 4 | # else 5 | # define CFFI_MESSAGEBOX 0 6 | # endif 7 | #endif 8 | 9 | 10 | #if CFFI_MESSAGEBOX 11 | /* Windows only: logic to take the Python-CFFI embedding logic 12 | initialization errors and display them in a background thread 13 | with MessageBox. The idea is that if the whole program closes 14 | as a result of this problem, then likely it is already a console 15 | program and you can read the stderr output in the console too. 16 | If it is not a console program, then it will likely show its own 17 | dialog to complain, or generally not abruptly close, and for this 18 | case the background thread should stay alive. 19 | */ 20 | static void *volatile _cffi_bootstrap_text; 21 | 22 | static PyObject *_cffi_start_error_capture(void) 23 | { 24 | PyObject *result = NULL; 25 | PyObject *x, *m, *bi; 26 | 27 | if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, 28 | (void *)1, NULL) != NULL) 29 | return (PyObject *)1; 30 | 31 | m = PyImport_AddModule("_cffi_error_capture"); 32 | if (m == NULL) 33 | goto error; 34 | 35 | result = PyModule_GetDict(m); 36 | if (result == NULL) 37 | goto error; 38 | 39 | #if PY_MAJOR_VERSION >= 3 40 | bi = PyImport_ImportModule("builtins"); 41 | #else 42 | bi = PyImport_ImportModule("__builtin__"); 43 | #endif 44 | if (bi == NULL) 45 | goto error; 46 | PyDict_SetItemString(result, "__builtins__", bi); 47 | Py_DECREF(bi); 48 | 49 | x = PyRun_String( 50 | "import sys\n" 51 | "class FileLike:\n" 52 | " def write(self, x):\n" 53 | " try:\n" 54 | " of.write(x)\n" 55 | " except: pass\n" 56 | " self.buf += x\n" 57 | " def flush(self):\n" 58 | " pass\n" 59 | "fl = FileLike()\n" 60 | "fl.buf = ''\n" 61 | "of = sys.stderr\n" 62 | "sys.stderr = fl\n" 63 | "def done():\n" 64 | " sys.stderr = of\n" 65 | " return fl.buf\n", /* make sure the returned value stays alive */ 66 | Py_file_input, 67 | result, result); 68 | Py_XDECREF(x); 69 | 70 | error: 71 | if (PyErr_Occurred()) 72 | { 73 | PyErr_WriteUnraisable(Py_None); 74 | PyErr_Clear(); 75 | } 76 | return result; 77 | } 78 | 79 | #pragma comment(lib, "user32.lib") 80 | 81 | static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) 82 | { 83 | Sleep(666); /* may be interrupted if the whole process is closing */ 84 | #if PY_MAJOR_VERSION >= 3 85 | MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, 86 | L"Python-CFFI error", 87 | MB_OK | MB_ICONERROR); 88 | #else 89 | MessageBoxA(NULL, (char *)_cffi_bootstrap_text, 90 | "Python-CFFI error", 91 | MB_OK | MB_ICONERROR); 92 | #endif 93 | _cffi_bootstrap_text = NULL; 94 | return 0; 95 | } 96 | 97 | static void _cffi_stop_error_capture(PyObject *ecap) 98 | { 99 | PyObject *s; 100 | void *text; 101 | 102 | if (ecap == (PyObject *)1) 103 | return; 104 | 105 | if (ecap == NULL) 106 | goto error; 107 | 108 | s = PyRun_String("done()", Py_eval_input, ecap, ecap); 109 | if (s == NULL) 110 | goto error; 111 | 112 | /* Show a dialog box, but in a background thread, and 113 | never show multiple dialog boxes at once. */ 114 | #if PY_MAJOR_VERSION >= 3 115 | text = PyUnicode_AsWideCharString(s, NULL); 116 | #else 117 | text = PyString_AsString(s); 118 | #endif 119 | 120 | _cffi_bootstrap_text = text; 121 | 122 | if (text != NULL) 123 | { 124 | HANDLE h; 125 | h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, 126 | NULL, 0, NULL); 127 | if (h != NULL) 128 | CloseHandle(h); 129 | } 130 | /* decref the string, but it should stay alive as 'fl.buf' 131 | in the small module above. It will really be freed only if 132 | we later get another similar error. So it's a leak of at 133 | most one copy of the small module. That's fine for this 134 | situation which is usually a "fatal error" anyway. */ 135 | Py_DECREF(s); 136 | PyErr_Clear(); 137 | return; 138 | 139 | error: 140 | _cffi_bootstrap_text = NULL; 141 | PyErr_Clear(); 142 | } 143 | 144 | #else 145 | 146 | static PyObject *_cffi_start_error_capture(void) { return NULL; } 147 | static void _cffi_stop_error_capture(PyObject *ecap) { } 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /src/cffi/_imp_emulation.py: -------------------------------------------------------------------------------- 1 | 2 | try: 3 | # this works on Python < 3.12 4 | from imp import * 5 | 6 | except ImportError: 7 | # this is a limited emulation for Python >= 3.12. 8 | # Note that this is used only for tests or for the old ffi.verify(). 9 | # This is copied from the source code of Python 3.11. 10 | 11 | from _imp import (acquire_lock, release_lock, 12 | is_builtin, is_frozen) 13 | 14 | from importlib._bootstrap import _load 15 | 16 | from importlib import machinery 17 | import os 18 | import sys 19 | import tokenize 20 | 21 | SEARCH_ERROR = 0 22 | PY_SOURCE = 1 23 | PY_COMPILED = 2 24 | C_EXTENSION = 3 25 | PY_RESOURCE = 4 26 | PKG_DIRECTORY = 5 27 | C_BUILTIN = 6 28 | PY_FROZEN = 7 29 | PY_CODERESOURCE = 8 30 | IMP_HOOK = 9 31 | 32 | def get_suffixes(): 33 | extensions = [(s, 'rb', C_EXTENSION) 34 | for s in machinery.EXTENSION_SUFFIXES] 35 | source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] 36 | bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] 37 | return extensions + source + bytecode 38 | 39 | def find_module(name, path=None): 40 | if not isinstance(name, str): 41 | raise TypeError("'name' must be a str, not {}".format(type(name))) 42 | elif not isinstance(path, (type(None), list)): 43 | # Backwards-compatibility 44 | raise RuntimeError("'path' must be None or a list, " 45 | "not {}".format(type(path))) 46 | 47 | if path is None: 48 | if is_builtin(name): 49 | return None, None, ('', '', C_BUILTIN) 50 | elif is_frozen(name): 51 | return None, None, ('', '', PY_FROZEN) 52 | else: 53 | path = sys.path 54 | 55 | for entry in path: 56 | package_directory = os.path.join(entry, name) 57 | for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: 58 | package_file_name = '__init__' + suffix 59 | file_path = os.path.join(package_directory, package_file_name) 60 | if os.path.isfile(file_path): 61 | return None, package_directory, ('', '', PKG_DIRECTORY) 62 | for suffix, mode, type_ in get_suffixes(): 63 | file_name = name + suffix 64 | file_path = os.path.join(entry, file_name) 65 | if os.path.isfile(file_path): 66 | break 67 | else: 68 | continue 69 | break # Break out of outer loop when breaking out of inner loop. 70 | else: 71 | raise ImportError(name, name=name) 72 | 73 | encoding = None 74 | if 'b' not in mode: 75 | with open(file_path, 'rb') as file: 76 | encoding = tokenize.detect_encoding(file.readline)[0] 77 | file = open(file_path, mode, encoding=encoding) 78 | return file, file_path, (suffix, mode, type_) 79 | 80 | def load_dynamic(name, path, file=None): 81 | loader = machinery.ExtensionFileLoader(name, path) 82 | spec = machinery.ModuleSpec(name=name, loader=loader, origin=path) 83 | return _load(spec) 84 | -------------------------------------------------------------------------------- /src/cffi/_shimmed_dist_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Temporary shim module to indirect the bits of distutils we need from setuptools/distutils while providing useful 3 | error messages beyond `No module named 'distutils' on Python >= 3.12, or when setuptools' vendored distutils is broken. 4 | 5 | This is a compromise to avoid a hard-dep on setuptools for Python >= 3.12, since many users don't need runtime compilation support from CFFI. 6 | """ 7 | import sys 8 | 9 | try: 10 | # import setuptools first; this is the most robust way to ensure its embedded distutils is available 11 | # (the .pth shim should usually work, but this is even more robust) 12 | import setuptools 13 | except Exception as ex: 14 | if sys.version_info >= (3, 12): 15 | # Python 3.12 has no built-in distutils to fall back on, so any import problem is fatal 16 | raise Exception("This CFFI feature requires setuptools on Python >= 3.12. The setuptools module is missing or non-functional.") from ex 17 | 18 | # silently ignore on older Pythons (support fallback to stdlib distutils where available) 19 | else: 20 | del setuptools 21 | 22 | try: 23 | # bring in just the bits of distutils we need, whether they really came from setuptools or stdlib-embedded distutils 24 | from distutils import log, sysconfig 25 | from distutils.ccompiler import CCompiler 26 | from distutils.command.build_ext import build_ext 27 | from distutils.core import Distribution, Extension 28 | from distutils.dir_util import mkpath 29 | from distutils.errors import DistutilsSetupError, CompileError, LinkError 30 | from distutils.log import set_threshold, set_verbosity 31 | 32 | if sys.platform == 'win32': 33 | try: 34 | # FUTURE: msvc9compiler module was removed in setuptools 74; consider removing, as it's only used by an ancient patch in `recompiler` 35 | from distutils.msvc9compiler import MSVCCompiler 36 | except ImportError: 37 | MSVCCompiler = None 38 | except Exception as ex: 39 | if sys.version_info >= (3, 12): 40 | raise Exception("This CFFI feature requires setuptools on Python >= 3.12. Please install the setuptools package.") from ex 41 | 42 | # anything older, just let the underlying distutils import error fly 43 | raise Exception("This CFFI feature requires distutils. Please install the distutils or setuptools package.") from ex 44 | 45 | del sys 46 | -------------------------------------------------------------------------------- /src/cffi/commontypes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from . import model 3 | from .error import FFIError 4 | 5 | 6 | COMMON_TYPES = {} 7 | 8 | try: 9 | # fetch "bool" and all simple Windows types 10 | from _cffi_backend import _get_common_types 11 | _get_common_types(COMMON_TYPES) 12 | except ImportError: 13 | pass 14 | 15 | COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') 16 | COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above 17 | COMMON_TYPES['float _Complex'] = '_cffi_float_complex_t' 18 | COMMON_TYPES['double _Complex'] = '_cffi_double_complex_t' 19 | 20 | for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: 21 | if _type.endswith('_t'): 22 | COMMON_TYPES[_type] = _type 23 | del _type 24 | 25 | _CACHE = {} 26 | 27 | def resolve_common_type(parser, commontype): 28 | try: 29 | return _CACHE[commontype] 30 | except KeyError: 31 | cdecl = COMMON_TYPES.get(commontype, commontype) 32 | if not isinstance(cdecl, str): 33 | result, quals = cdecl, 0 # cdecl is already a BaseType 34 | elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: 35 | result, quals = model.PrimitiveType(cdecl), 0 36 | elif cdecl == 'set-unicode-needed': 37 | raise FFIError("The Windows type %r is only available after " 38 | "you call ffi.set_unicode()" % (commontype,)) 39 | else: 40 | if commontype == cdecl: 41 | raise FFIError( 42 | "Unsupported type: %r. Please look at " 43 | "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " 44 | "and file an issue if you think this type should really " 45 | "be supported." % (commontype,)) 46 | result, quals = parser.parse_type_and_quals(cdecl) # recursive 47 | 48 | assert isinstance(result, model.BaseTypeByIdentity) 49 | _CACHE[commontype] = result, quals 50 | return result, quals 51 | 52 | 53 | # ____________________________________________________________ 54 | # extra types for Windows (most of them are in commontypes.c) 55 | 56 | 57 | def win_common_types(): 58 | return { 59 | "UNICODE_STRING": model.StructType( 60 | "_UNICODE_STRING", 61 | ["Length", 62 | "MaximumLength", 63 | "Buffer"], 64 | [model.PrimitiveType("unsigned short"), 65 | model.PrimitiveType("unsigned short"), 66 | model.PointerType(model.PrimitiveType("wchar_t"))], 67 | [-1, -1, -1]), 68 | "PUNICODE_STRING": "UNICODE_STRING *", 69 | "PCUNICODE_STRING": "const UNICODE_STRING *", 70 | 71 | "TBYTE": "set-unicode-needed", 72 | "TCHAR": "set-unicode-needed", 73 | "LPCTSTR": "set-unicode-needed", 74 | "PCTSTR": "set-unicode-needed", 75 | "LPTSTR": "set-unicode-needed", 76 | "PTSTR": "set-unicode-needed", 77 | "PTBYTE": "set-unicode-needed", 78 | "PTCHAR": "set-unicode-needed", 79 | } 80 | 81 | if sys.platform == 'win32': 82 | COMMON_TYPES.update(win_common_types()) 83 | -------------------------------------------------------------------------------- /src/cffi/error.py: -------------------------------------------------------------------------------- 1 | 2 | class FFIError(Exception): 3 | __module__ = 'cffi' 4 | 5 | class CDefError(Exception): 6 | __module__ = 'cffi' 7 | def __str__(self): 8 | try: 9 | current_decl = self.args[1] 10 | filename = current_decl.coord.file 11 | linenum = current_decl.coord.line 12 | prefix = '%s:%d: ' % (filename, linenum) 13 | except (AttributeError, TypeError, IndexError): 14 | prefix = '' 15 | return '%s%s' % (prefix, self.args[0]) 16 | 17 | class VerificationError(Exception): 18 | """ An error raised when verification fails 19 | """ 20 | __module__ = 'cffi' 21 | 22 | class VerificationMissing(Exception): 23 | """ An error raised when incomplete structures are passed into 24 | cdef, but no verification has been done 25 | """ 26 | __module__ = 'cffi' 27 | 28 | class PkgConfigError(Exception): 29 | """ An error raised for missing modules in pkg-config 30 | """ 31 | __module__ = 'cffi' 32 | -------------------------------------------------------------------------------- /src/cffi/ffiplatform.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | from .error import VerificationError 3 | 4 | 5 | LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', 6 | 'extra_objects', 'depends'] 7 | 8 | def get_extension(srcfilename, modname, sources=(), **kwds): 9 | from cffi._shimmed_dist_utils import Extension 10 | allsources = [srcfilename] 11 | for src in sources: 12 | allsources.append(os.path.normpath(src)) 13 | return Extension(name=modname, sources=allsources, **kwds) 14 | 15 | def compile(tmpdir, ext, compiler_verbose=0, debug=None): 16 | """Compile a C extension module using distutils.""" 17 | 18 | saved_environ = os.environ.copy() 19 | try: 20 | outputfilename = _build(tmpdir, ext, compiler_verbose, debug) 21 | outputfilename = os.path.abspath(outputfilename) 22 | finally: 23 | # workaround for a distutils bugs where some env vars can 24 | # become longer and longer every time it is used 25 | for key, value in saved_environ.items(): 26 | if os.environ.get(key) != value: 27 | os.environ[key] = value 28 | return outputfilename 29 | 30 | def _build(tmpdir, ext, compiler_verbose=0, debug=None): 31 | # XXX compact but horrible :-( 32 | from cffi._shimmed_dist_utils import Distribution, CompileError, LinkError, set_threshold, set_verbosity 33 | 34 | dist = Distribution({'ext_modules': [ext]}) 35 | dist.parse_config_files() 36 | options = dist.get_option_dict('build_ext') 37 | if debug is None: 38 | debug = sys.flags.debug 39 | options['debug'] = ('ffiplatform', debug) 40 | options['force'] = ('ffiplatform', True) 41 | options['build_lib'] = ('ffiplatform', tmpdir) 42 | options['build_temp'] = ('ffiplatform', tmpdir) 43 | # 44 | try: 45 | old_level = set_threshold(0) or 0 46 | try: 47 | set_verbosity(compiler_verbose) 48 | dist.run_command('build_ext') 49 | cmd_obj = dist.get_command_obj('build_ext') 50 | [soname] = cmd_obj.get_outputs() 51 | finally: 52 | set_threshold(old_level) 53 | except (CompileError, LinkError) as e: 54 | raise VerificationError('%s: %s' % (e.__class__.__name__, e)) 55 | # 56 | return soname 57 | 58 | try: 59 | from os.path import samefile 60 | except ImportError: 61 | def samefile(f1, f2): 62 | return os.path.abspath(f1) == os.path.abspath(f2) 63 | 64 | def maybe_relative_path(path): 65 | if not os.path.isabs(path): 66 | return path # already relative 67 | dir = path 68 | names = [] 69 | while True: 70 | prevdir = dir 71 | dir, name = os.path.split(prevdir) 72 | if dir == prevdir or not dir: 73 | return path # failed to make it relative 74 | names.append(name) 75 | try: 76 | if samefile(dir, os.curdir): 77 | names.reverse() 78 | return os.path.join(*names) 79 | except OSError: 80 | pass 81 | 82 | # ____________________________________________________________ 83 | 84 | try: 85 | int_or_long = (int, long) 86 | import cStringIO 87 | except NameError: 88 | int_or_long = int # Python 3 89 | import io as cStringIO 90 | 91 | def _flatten(x, f): 92 | if isinstance(x, str): 93 | f.write('%ds%s' % (len(x), x)) 94 | elif isinstance(x, dict): 95 | keys = sorted(x.keys()) 96 | f.write('%dd' % len(keys)) 97 | for key in keys: 98 | _flatten(key, f) 99 | _flatten(x[key], f) 100 | elif isinstance(x, (list, tuple)): 101 | f.write('%dl' % len(x)) 102 | for value in x: 103 | _flatten(value, f) 104 | elif isinstance(x, int_or_long): 105 | f.write('%di' % (x,)) 106 | else: 107 | raise TypeError( 108 | "the keywords to verify() contains unsupported object %r" % (x,)) 109 | 110 | def flatten(x): 111 | f = cStringIO.StringIO() 112 | _flatten(x, f) 113 | return f.getvalue() 114 | -------------------------------------------------------------------------------- /src/cffi/lock.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if sys.version_info < (3,): 4 | try: 5 | from thread import allocate_lock 6 | except ImportError: 7 | from dummy_thread import allocate_lock 8 | else: 9 | try: 10 | from _thread import allocate_lock 11 | except ImportError: 12 | from _dummy_thread import allocate_lock 13 | 14 | 15 | ##import sys 16 | ##l1 = allocate_lock 17 | 18 | ##class allocate_lock(object): 19 | ## def __init__(self): 20 | ## self._real = l1() 21 | ## def __enter__(self): 22 | ## for i in range(4, 0, -1): 23 | ## print sys._getframe(i).f_code 24 | ## print 25 | ## return self._real.__enter__() 26 | ## def __exit__(self, *args): 27 | ## return self._real.__exit__(*args) 28 | ## def acquire(self, f): 29 | ## assert f is False 30 | ## return self._real.acquire(f) 31 | -------------------------------------------------------------------------------- /src/cffi/pkgconfig.py: -------------------------------------------------------------------------------- 1 | # pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi 2 | import sys, os, subprocess 3 | 4 | from .error import PkgConfigError 5 | 6 | 7 | def merge_flags(cfg1, cfg2): 8 | """Merge values from cffi config flags cfg2 to cf1 9 | 10 | Example: 11 | merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) 12 | {"libraries": ["one", "two"]} 13 | """ 14 | for key, value in cfg2.items(): 15 | if key not in cfg1: 16 | cfg1[key] = value 17 | else: 18 | if not isinstance(cfg1[key], list): 19 | raise TypeError("cfg1[%r] should be a list of strings" % (key,)) 20 | if not isinstance(value, list): 21 | raise TypeError("cfg2[%r] should be a list of strings" % (key,)) 22 | cfg1[key].extend(value) 23 | return cfg1 24 | 25 | 26 | def call(libname, flag, encoding=sys.getfilesystemencoding()): 27 | """Calls pkg-config and returns the output if found 28 | """ 29 | a = ["pkg-config", "--print-errors"] 30 | a.append(flag) 31 | a.append(libname) 32 | try: 33 | pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 34 | except EnvironmentError as e: 35 | raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) 36 | 37 | bout, berr = pc.communicate() 38 | if pc.returncode != 0: 39 | try: 40 | berr = berr.decode(encoding) 41 | except Exception: 42 | pass 43 | raise PkgConfigError(berr.strip()) 44 | 45 | if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x 46 | try: 47 | bout = bout.decode(encoding) 48 | except UnicodeDecodeError: 49 | raise PkgConfigError("pkg-config %s %s returned bytes that cannot " 50 | "be decoded with encoding %r:\n%r" % 51 | (flag, libname, encoding, bout)) 52 | 53 | if os.altsep != '\\' and '\\' in bout: 54 | raise PkgConfigError("pkg-config %s %s returned an unsupported " 55 | "backslash-escaped output:\n%r" % 56 | (flag, libname, bout)) 57 | return bout 58 | 59 | 60 | def flags_from_pkgconfig(libs): 61 | r"""Return compiler line flags for FFI.set_source based on pkg-config output 62 | 63 | Usage 64 | ... 65 | ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) 66 | 67 | If pkg-config is installed on build machine, then arguments include_dirs, 68 | library_dirs, libraries, define_macros, extra_compile_args and 69 | extra_link_args are extended with an output of pkg-config for libfoo and 70 | libbar. 71 | 72 | Raises PkgConfigError in case the pkg-config call fails. 73 | """ 74 | 75 | def get_include_dirs(string): 76 | return [x[2:] for x in string.split() if x.startswith("-I")] 77 | 78 | def get_library_dirs(string): 79 | return [x[2:] for x in string.split() if x.startswith("-L")] 80 | 81 | def get_libraries(string): 82 | return [x[2:] for x in string.split() if x.startswith("-l")] 83 | 84 | # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils 85 | def get_macros(string): 86 | def _macro(x): 87 | x = x[2:] # drop "-D" 88 | if '=' in x: 89 | return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") 90 | else: 91 | return (x, None) # "-Dfoo" => ("foo", None) 92 | return [_macro(x) for x in string.split() if x.startswith("-D")] 93 | 94 | def get_other_cflags(string): 95 | return [x for x in string.split() if not x.startswith("-I") and 96 | not x.startswith("-D")] 97 | 98 | def get_other_libs(string): 99 | return [x for x in string.split() if not x.startswith("-L") and 100 | not x.startswith("-l")] 101 | 102 | # return kwargs for given libname 103 | def kwargs(libname): 104 | fse = sys.getfilesystemencoding() 105 | all_cflags = call(libname, "--cflags") 106 | all_libs = call(libname, "--libs") 107 | return { 108 | "include_dirs": get_include_dirs(all_cflags), 109 | "library_dirs": get_library_dirs(all_libs), 110 | "libraries": get_libraries(all_libs), 111 | "define_macros": get_macros(all_cflags), 112 | "extra_compile_args": get_other_cflags(all_cflags), 113 | "extra_link_args": get_other_libs(all_libs), 114 | } 115 | 116 | # merge all arguments together 117 | ret = {} 118 | for libname in libs: 119 | lib_flags = kwargs(libname) 120 | merge_flags(ret, lib_flags) 121 | return ret 122 | -------------------------------------------------------------------------------- /testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/testing/__init__.py -------------------------------------------------------------------------------- /testing/cffi0/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/testing/cffi0/__init__.py -------------------------------------------------------------------------------- /testing/cffi0/callback_in_thread.py: -------------------------------------------------------------------------------- 1 | import sys, time 2 | sys.path.insert(0, sys.argv[1]) 3 | from cffi import FFI 4 | 5 | def _run_callback_in_thread(): 6 | ffi = FFI() 7 | ffi.cdef(""" 8 | typedef int (*mycallback_func_t)(int, int); 9 | int threaded_ballback_test(mycallback_func_t mycb); 10 | """) 11 | lib = ffi.verify(""" 12 | #include 13 | typedef int (*mycallback_func_t)(int, int); 14 | void *my_wait_function(void *ptr) { 15 | mycallback_func_t cbfunc = (mycallback_func_t)ptr; 16 | cbfunc(10, 10); 17 | cbfunc(12, 15); 18 | return NULL; 19 | } 20 | int threaded_ballback_test(mycallback_func_t mycb) { 21 | pthread_t thread; 22 | pthread_create(&thread, NULL, my_wait_function, (void*)mycb); 23 | return 0; 24 | } 25 | """, extra_compile_args=['-pthread']) 26 | seen = [] 27 | @ffi.callback('int(*)(int,int)') 28 | def mycallback(x, y): 29 | time.sleep(0.022) 30 | seen.append((x, y)) 31 | return 0 32 | lib.threaded_ballback_test(mycallback) 33 | count = 300 34 | while len(seen) != 2: 35 | time.sleep(0.01) 36 | count -= 1 37 | assert count > 0, "timeout" 38 | assert seen == [(10, 10), (12, 15)] 39 | 40 | print('STARTING') 41 | _run_callback_in_thread() 42 | print('DONE') 43 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_module/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from distutils.core import setup 3 | import snip_basic_verify 4 | 5 | setup( 6 | py_modules=['snip_basic_verify'], 7 | ext_modules=[snip_basic_verify.ffi.verifier.get_extension()]) 8 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_module/snip_basic_verify.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 18 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_package_1/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from distutils.core import setup 3 | import snip_basic_verify1 4 | 5 | setup( 6 | packages=['snip_basic_verify1'], 7 | ext_modules=[snip_basic_verify1.ffi.verifier.get_extension()]) 8 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_package_1/snip_basic_verify1/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 18 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_package_2/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from distutils.core import setup 3 | import snip_basic_verify2 4 | 5 | setup( 6 | packages=['snip_basic_verify2'], 7 | ext_package='snip_basic_verify2', 8 | ext_modules=[snip_basic_verify2.ffi.verifier.get_extension()]) 9 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/distutils_package_2/snip_basic_verify2/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | ext_package='snip_basic_verify2', 18 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 19 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/infrastructure/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from distutils.core import setup 3 | 4 | setup(packages=['snip_infrastructure'], 5 | requires=['cffi']) 6 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/infrastructure/snip_infrastructure/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | def func(): 3 | return 42 4 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_module/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | import snip_setuptools_verify 4 | 5 | setup( 6 | zip_safe=False, 7 | py_modules=['snip_setuptools_verify'], 8 | ext_modules=[snip_setuptools_verify.ffi.verifier.get_extension()]) 9 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_module/snip_setuptools_verify.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 18 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_package_1/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | import snip_setuptools_verify1 4 | 5 | setup( 6 | zip_safe=False, 7 | packages=['snip_setuptools_verify1'], 8 | ext_modules=[snip_setuptools_verify1.ffi.verifier.get_extension()]) 9 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_package_1/snip_setuptools_verify1/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 18 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_package_2/setup.py: -------------------------------------------------------------------------------- 1 | 2 | from setuptools import setup 3 | import snip_setuptools_verify2 4 | 5 | setup( 6 | zip_safe=False, 7 | packages=['snip_setuptools_verify2'], 8 | ext_package='snip_setuptools_verify2', 9 | ext_modules=[snip_setuptools_verify2.ffi.verifier.get_extension()]) 10 | -------------------------------------------------------------------------------- /testing/cffi0/snippets/setuptools_package_2/snip_setuptools_verify2/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from cffi import FFI 3 | import sys 4 | 5 | ffi = FFI() 6 | ffi.cdef(""" // some declarations from the man page 7 | struct passwd { 8 | char *pw_name; 9 | ...; 10 | }; 11 | struct passwd *getpwuid(int uid); 12 | """) 13 | C = ffi.verify(""" // passed to the real C compiler 14 | #include 15 | #include 16 | """, libraries=[], # or a list of libraries to link with 17 | ext_package='snip_setuptools_verify2', 18 | force_generic_engine=hasattr(sys, '_force_generic_engine_')) 19 | -------------------------------------------------------------------------------- /testing/cffi0/test_cdata.py: -------------------------------------------------------------------------------- 1 | from cffi import FFI 2 | 3 | class FakeBackend(object): 4 | 5 | def nonstandard_integer_types(self): 6 | return {} 7 | 8 | def sizeof(self, name): 9 | return 1 10 | 11 | def load_library(self, path): 12 | return "fake library" 13 | 14 | def new_primitive_type(self, name): 15 | return FakeType("primitive " + name) 16 | 17 | def new_void_type(self): 18 | return FakeType("void") 19 | def new_pointer_type(self, x): 20 | return FakeType('ptr-to-%r' % (x,)) 21 | def new_array_type(self, x, y): 22 | return FakeType('array-from-%r-len-%r' % (x, y)) 23 | def cast(self, x, y): 24 | return 'casted!' 25 | def _get_types(self): 26 | return "CData", "CType" 27 | 28 | buffer = "buffer type" 29 | 30 | 31 | class FakeType(object): 32 | def __init__(self, cdecl): 33 | self.cdecl = cdecl 34 | 35 | 36 | def test_typeof(): 37 | ffi = FFI(backend=FakeBackend()) 38 | clong = ffi.typeof("signed long int") 39 | assert isinstance(clong, FakeType) 40 | assert clong.cdecl == 'primitive long' 41 | -------------------------------------------------------------------------------- /testing/cffi0/test_ctypes.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | from testing.cffi0 import backend_tests 4 | from cffi.backend_ctypes import CTypesBackend 5 | 6 | 7 | class TestCTypes(backend_tests.BackendTests): 8 | # for individual tests see 9 | # ====> backend_tests.py 10 | 11 | Backend = CTypesBackend 12 | TypeRepr = "'>" 13 | 14 | def test_array_of_func_ptr(self): 15 | pytest.skip("ctypes backend: not supported: " 16 | "initializers for function pointers") 17 | 18 | def test_structptr_argument(self): 19 | pytest.skip("ctypes backend: not supported: passing a list " 20 | "for a pointer argument") 21 | 22 | def test_array_argument_as_list(self): 23 | pytest.skip("ctypes backend: not supported: passing a list " 24 | "for a pointer argument") 25 | 26 | def test_cast_to_array_type(self): 27 | pytest.skip("ctypes backend: not supported: casting to array") 28 | 29 | def test_nested_anonymous_struct(self): 30 | pytest.skip("ctypes backend: not supported: nested anonymous struct") 31 | 32 | def test_nested_field_offset_align(self): 33 | pytest.skip("ctypes backend: not supported: nested anonymous struct") 34 | 35 | def test_nested_anonymous_union(self): 36 | pytest.skip("ctypes backend: not supported: nested anonymous union") 37 | 38 | def test_nested_anonymous_struct_2(self): 39 | pytest.skip("ctypes backend: not supported: nested anonymous union") 40 | 41 | def test_CData_CType_2(self): 42 | if sys.version_info >= (3,): 43 | pytest.skip("ctypes backend: not supported in Python 3: CType") 44 | backend_tests.BackendTests.test_CData_CType_2(self) 45 | -------------------------------------------------------------------------------- /testing/cffi0/test_model.py: -------------------------------------------------------------------------------- 1 | from cffi.model import * 2 | 3 | 4 | def test_void_type(): 5 | assert void_type.get_c_name() == "void" 6 | assert void_type.get_c_name("foo") == "void foo" 7 | assert void_type.get_c_name("*foo") == "void *foo" 8 | 9 | def test_primitive_type(): 10 | int_type = PrimitiveType("int") 11 | assert int_type.get_c_name() == "int" 12 | assert int_type.get_c_name("foo") == "int foo" 13 | assert int_type.get_c_name("*foo") == "int *foo" 14 | assert int_type.get_c_name("[5]") == "int[5]" 15 | 16 | def test_raw_function_type(): 17 | int_type = PrimitiveType("int") 18 | fn_type = RawFunctionType([], int_type, False) 19 | assert fn_type.get_c_name() == "int()(void)" 20 | assert fn_type.get_c_name("*") == "int( *)(void)" 21 | assert fn_type.get_c_name("*foo") == "int( *foo)(void)" 22 | fn_type = RawFunctionType([int_type], int_type, False) 23 | assert fn_type.get_c_name() == "int()(int)" 24 | fn_type = RawFunctionType([int_type] * 2, int_type, False) 25 | assert fn_type.get_c_name() == "int()(int, int)" 26 | # 27 | fn_type = RawFunctionType([int_type], int_type, True) 28 | assert fn_type.get_c_name() == "int()(int, ...)" 29 | assert fn_type.get_c_name("*foo") == "int( *foo)(int, ...)" 30 | # 31 | res_type = FunctionPtrType([int_type], int_type, True) 32 | fn_type = RawFunctionType([int_type], res_type, True) 33 | assert fn_type.get_c_name("x") == "int(*( x)(int, ...))(int, ...)" 34 | 35 | def test_function_ptr_type(): 36 | int_type = PrimitiveType("int") 37 | fn_type = FunctionPtrType([], int_type, False) 38 | assert fn_type.get_c_name() == "int(*)(void)" 39 | assert fn_type.get_c_name("*") == "int(* *)(void)" 40 | assert fn_type.get_c_name("*foo") == "int(* *foo)(void)" 41 | fn_type = FunctionPtrType([int_type], int_type, False) 42 | assert fn_type.get_c_name() == "int(*)(int)" 43 | fn_type = FunctionPtrType([int_type] * 2, int_type, False) 44 | assert fn_type.get_c_name() == "int(*)(int, int)" 45 | # 46 | fn_type = FunctionPtrType([int_type], int_type, True) 47 | assert fn_type.get_c_name() == "int(*)(int, ...)" 48 | 49 | def test_pointer_type(): 50 | ptr_type = PointerType(PrimitiveType("int")) 51 | assert ptr_type.get_c_name("x") == "int * x" 52 | 53 | def test_const_pointer_type(): 54 | ptr_type = ConstPointerType(PrimitiveType("int")) 55 | assert ptr_type.get_c_name("x") == "int const * x" 56 | ptr_type = ConstPointerType(ArrayType(PrimitiveType("int"), 5)) 57 | assert ptr_type.get_c_name("") == "int const (*)[5]" 58 | assert ptr_type.get_c_name("*x") == "int const (* *x)[5]" 59 | ptr_type = ConstPointerType(ArrayType( 60 | ConstPointerType(PrimitiveType("int")), 5)) 61 | assert ptr_type.get_c_name("x") == "int const * const (* x)[5]" 62 | ptr_type = PointerType(ArrayType( 63 | ConstPointerType(PrimitiveType("int")), 5)) 64 | assert ptr_type.get_c_name("x") == "int const *(* x)[5]" 65 | ptr_type = ConstPointerType(ArrayType( 66 | PointerType(PrimitiveType("int")), 5)) 67 | assert ptr_type.get_c_name("x") == "int * const (* x)[5]" 68 | 69 | def test_qual_pointer_type(): 70 | ptr_type = PointerType(PrimitiveType("long long"), Q_RESTRICT) 71 | assert ptr_type.get_c_name("") == "long long __restrict *" 72 | assert const_voidp_type.get_c_name("") == "void const *" 73 | 74 | def test_unknown_pointer_type(): 75 | ptr_type = unknown_ptr_type("foo_p") 76 | assert ptr_type.get_c_name("") == "foo_p" 77 | assert ptr_type.get_c_name("x") == "foo_p x" 78 | 79 | def test_unknown_type(): 80 | u_type = unknown_type("foo_t") 81 | assert u_type.get_c_name("") == "foo_t" 82 | assert u_type.get_c_name("x") == "foo_t x" 83 | 84 | def test_array_type(): 85 | a_type = ArrayType(PrimitiveType("int"), None) 86 | assert a_type.get_c_name("") == "int[]" 87 | assert a_type.get_c_name("x") == "int x[]" 88 | assert a_type.get_c_name("*x") == "int(*x)[]" 89 | assert a_type.get_c_name(" *x") == "int(*x)[]" 90 | assert a_type.get_c_name("[5]") == "int[5][]" 91 | a_type = ArrayType(unknown_type("foo_t"), 5) 92 | assert a_type.get_c_name("") == "foo_t[5]" 93 | assert a_type.get_c_name("x") == "foo_t x[5]" 94 | assert a_type.get_c_name("*x") == "foo_t(*x)[5]" 95 | a_type = ArrayType(unknown_ptr_type("foo_p"), None) 96 | assert a_type.get_c_name("") == "foo_p[]" 97 | assert a_type.get_c_name("x") == "foo_p x[]" 98 | assert a_type.get_c_name("*x") == "foo_p(*x)[]" 99 | a_type = ArrayType(ConstPointerType(PrimitiveType("int")), None) 100 | assert a_type.get_c_name("") == "int const *[]" 101 | assert a_type.get_c_name("x") == "int const * x[]" 102 | assert a_type.get_c_name("*x") == "int const *(*x)[]" 103 | fn_type = FunctionPtrType([], PrimitiveType("int"), False) 104 | a_type = ArrayType(fn_type, 5) 105 | assert a_type.get_c_name("") == "int(*[5])(void)" 106 | assert a_type.get_c_name("x") == "int(* x[5])(void)" 107 | assert a_type.get_c_name("*x") == "int(*(*x)[5])(void)" 108 | 109 | def test_struct_type(): 110 | struct_type = StructType("foo_s", None, None, None) 111 | assert struct_type.get_c_name() == "struct foo_s" 112 | assert struct_type.get_c_name("*x") == "struct foo_s *x" 113 | 114 | def test_union_type(): 115 | union_type = UnionType("foo_s", None, None, None) 116 | assert union_type.get_c_name() == "union foo_s" 117 | 118 | def test_enum_type(): 119 | enum_type = EnumType("foo_e", [], []) 120 | assert enum_type.get_c_name() == "enum foo_e" 121 | -------------------------------------------------------------------------------- /testing/cffi0/test_platform.py: -------------------------------------------------------------------------------- 1 | import os 2 | from cffi.ffiplatform import maybe_relative_path, flatten 3 | 4 | 5 | def test_not_absolute(): 6 | assert maybe_relative_path('foo/bar') == 'foo/bar' 7 | assert maybe_relative_path('test_platform.py') == 'test_platform.py' 8 | 9 | def test_different_absolute(): 10 | p = os.path.join('..', 'baz.py') 11 | assert maybe_relative_path(p) == p 12 | 13 | def test_absolute_mapping(): 14 | p = os.path.abspath('baz.py') 15 | assert maybe_relative_path(p) == 'baz.py' 16 | foobaz = os.path.join('foo', 'baz.py') 17 | assert maybe_relative_path(os.path.abspath(foobaz)) == foobaz 18 | 19 | def test_flatten(): 20 | assert flatten("foo") == "3sfoo" 21 | assert flatten(-10000000000000000000000000000) == \ 22 | "-10000000000000000000000000000i" 23 | assert flatten([4, 5]) == "2l4i5i" 24 | assert flatten({4: 5}) == "1d4i5i" 25 | assert flatten({"foo": ("bar", "baaz")}) == "1d3sfoo2l3sbar4sbaaz" 26 | -------------------------------------------------------------------------------- /testing/cffi0/test_unicode_literals.py: -------------------------------------------------------------------------------- 1 | # 2 | # ---------------------------------------------- 3 | # WARNING, ALL LITERALS IN THIS FILE ARE UNICODE 4 | # ---------------------------------------------- 5 | # 6 | from __future__ import unicode_literals 7 | # 8 | # 9 | # 10 | import sys, math 11 | from cffi import FFI 12 | from testing.support import is_musl 13 | 14 | 15 | lib_m = "m" 16 | if sys.platform == 'win32': 17 | #there is a small chance this fails on Mingw via environ $CC 18 | import distutils.ccompiler 19 | if distutils.ccompiler.get_default_compiler() == 'msvc': 20 | lib_m = 'msvcrt' 21 | elif is_musl: 22 | lib_m = 'c' 23 | 24 | 25 | def test_cast(): 26 | ffi = FFI() 27 | assert int(ffi.cast("int", 3.14)) == 3 # unicode literal 28 | 29 | def test_new(): 30 | ffi = FFI() 31 | assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal 32 | 33 | def test_typeof(): 34 | ffi = FFI() 35 | tp = ffi.typeof("int[51]") # unicode literal 36 | assert tp.length == 51 37 | 38 | def test_sizeof(): 39 | ffi = FFI() 40 | assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal 41 | 42 | def test_alignof(): 43 | ffi = FFI() 44 | assert ffi.alignof("int[51]") == 4 # unicode literal 45 | 46 | def test_getctype(): 47 | ffi = FFI() 48 | assert ffi.getctype("int**") == "int * *" # unicode literal 49 | assert type(ffi.getctype("int**")) is str 50 | 51 | def test_cdef(): 52 | ffi = FFI() 53 | ffi.cdef("typedef int foo_t[50];") # unicode literal 54 | 55 | def test_offsetof(): 56 | ffi = FFI() 57 | ffi.cdef("typedef struct { int x, y; } foo_t;") 58 | assert ffi.offsetof("foo_t", "y") == 4 # unicode literal 59 | 60 | def test_enum(): 61 | ffi = FFI() 62 | ffi.cdef("enum foo_e { AA, BB, CC };") # unicode literal 63 | x = ffi.cast("enum foo_e", 1) 64 | assert int(ffi.cast("int", x)) == 1 65 | 66 | def test_dlopen(): 67 | ffi = FFI() 68 | ffi.cdef("double sin(double x);") 69 | m = ffi.dlopen(lib_m) # unicode literal 70 | x = m.sin(1.23) 71 | assert x == math.sin(1.23) 72 | 73 | def test_verify(): 74 | ffi = FFI() 75 | ffi.cdef("double test_verify_1(double x);") # unicode literal 76 | lib = ffi.verify("double test_verify_1(double x) { return x * 42.0; }") 77 | assert lib.test_verify_1(-1.5) == -63.0 78 | 79 | def test_callback(): 80 | ffi = FFI() 81 | cb = ffi.callback("int(int)", # unicode literal 82 | lambda x: x + 42) 83 | assert cb(5) == 47 84 | -------------------------------------------------------------------------------- /testing/cffi0/test_verify2.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from .test_verify import * 3 | 4 | # eliminate warning noise from common test modules that are repeatedly re-imported 5 | pytestmark = pytest.mark.filterwarnings("ignore:reimporting:UserWarning") 6 | 7 | # This test file runs normally after test_verify. We only clean up the .c 8 | # sources, to check that it also works when we have only the .so. The 9 | # tests should run much faster than test_verify. 10 | 11 | def setup_module(): 12 | import cffi.verifier 13 | cffi.verifier.cleanup_tmpdir(keep_so=True) 14 | -------------------------------------------------------------------------------- /testing/cffi0/test_version.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | import pytest 3 | import cffi, _cffi_backend 4 | from pathlib import Path 5 | 6 | def setup_module(mod): 7 | if '_cffi_backend' in sys.builtin_module_names: 8 | pytest.skip("this is embedded version") 9 | 10 | #BACKEND_VERSIONS = { 11 | # '0.4.2': '0.4', # did not change 12 | # '0.7.1': '0.7', # did not change 13 | # '0.7.2': '0.7', # did not change 14 | # '0.8.1': '0.8', # did not change (essentially) 15 | # '0.8.4': '0.8.3', # did not change 16 | # } 17 | 18 | def _read(p): 19 | with open(p) as f: 20 | return f.read() 21 | 22 | def test_version(): 23 | v = cffi.__version__ 24 | version_info = '.'.join(str(i) for i in cffi.__version_info__) 25 | version_info = version_info.replace('.beta.', 'b') 26 | version_info = version_info.replace('.plus', '+') 27 | version_info = version_info.replace('.rc', 'rc') 28 | assert v == version_info 29 | #v = BACKEND_VERSIONS.get(v, v) 30 | assert v == _cffi_backend.__version__ 31 | 32 | def test_doc_version(): 33 | cffi_root = Path(os.path.dirname(__file__)).parent.parent 34 | p = cffi_root / 'doc/source/conf.py' 35 | content = _read(p) 36 | # 37 | v = cffi.__version__ 38 | assert ("version = '%s'\n" % v[:4]) in content 39 | assert ("release = '%s'\n" % v) in content 40 | 41 | def test_pyproject_version(): 42 | cffi_root = Path(os.path.dirname(__file__)).parent.parent 43 | p = cffi_root / 'pyproject.toml' 44 | content = _read(p) 45 | # 46 | v = cffi.__version__.replace('+', '') 47 | assert f'version = "{v}"' in content 48 | 49 | def test_c_version(): 50 | cffi_root = Path(os.path.dirname(__file__)).parent.parent 51 | v = cffi.__version__ 52 | p = cffi_root / 'src/c/test_c.py' 53 | content = _read(p) 54 | #v = BACKEND_VERSIONS.get(v, v) 55 | assert (('assert __version__ == "%s"' % v) in content) 56 | 57 | def test_embedding_h(): 58 | cffi_root = Path(os.path.dirname(__file__)).parent.parent 59 | v = cffi.__version__ 60 | p = cffi_root / 'src/cffi/_embedding.h' 61 | content = _read(p) 62 | assert ('cffi version: %s"' % (v,)) in content 63 | -------------------------------------------------------------------------------- /testing/cffi0/test_vgen.py: -------------------------------------------------------------------------------- 1 | import cffi.verifier 2 | from .test_verify import * 3 | 4 | 5 | def setup_module(): 6 | cffi.verifier.cleanup_tmpdir() 7 | cffi.verifier._FORCE_GENERIC_ENGINE = True 8 | # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we 9 | # also test vengine_gen.py. 10 | 11 | def teardown_module(): 12 | cffi.verifier._FORCE_GENERIC_ENGINE = False 13 | -------------------------------------------------------------------------------- /testing/cffi0/test_vgen2.py: -------------------------------------------------------------------------------- 1 | import cffi.verifier 2 | from .test_vgen import * 3 | 4 | # This test file runs normally after test_vgen. We only clean up the .c 5 | # sources, to check that it also works when we have only the .so. The 6 | # tests should run much faster than test_vgen. 7 | 8 | def setup_module(): 9 | cffi.verifier.cleanup_tmpdir(keep_so=True) 10 | cffi.verifier._FORCE_GENERIC_ENGINE = True 11 | 12 | def teardown_module(): 13 | cffi.verifier._FORCE_GENERIC_ENGINE = False 14 | -------------------------------------------------------------------------------- /testing/cffi1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/testing/cffi1/__init__.py -------------------------------------------------------------------------------- /testing/cffi1/test_cffi_binary.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | import pytest 3 | import _cffi_backend 4 | from testing.support import is_musl 5 | 6 | def test_no_unknown_exported_symbols(): 7 | if not hasattr(_cffi_backend, '__file__'): 8 | pytest.skip("_cffi_backend module is built-in") 9 | if not sys.platform.startswith('linux') or is_musl: 10 | pytest.skip("linux-only") 11 | g = os.popen("objdump -T '%s'" % _cffi_backend.__file__, 'r') 12 | for line in g: 13 | if not line.startswith('0'): 14 | continue 15 | if line[line.find(' ') + 1] == 'l': 16 | continue 17 | if '*UND*' in line: 18 | continue 19 | name = line.split()[-1] 20 | if name.startswith('_') or name.startswith('.'): 21 | continue 22 | # a statically-linked libffi will always appear here without header hackage, ignore it if it's internal 23 | if name.startswith('ffi_') and 'Base' in line: 24 | continue 25 | if name not in ('init_cffi_backend', 'PyInit__cffi_backend', 'cffistatic_ffi_call'): 26 | raise Exception("Unexpected exported name %r" % (name,)) 27 | g.close() 28 | -------------------------------------------------------------------------------- /testing/cffi1/test_commontypes.py: -------------------------------------------------------------------------------- 1 | import os, cffi, re 2 | import pytest 3 | import _cffi_backend 4 | 5 | 6 | def getlines(): 7 | try: 8 | f = open(os.path.join(os.path.dirname(cffi.__file__), 9 | '..', 'c', 'commontypes.c')) 10 | except IOError: 11 | pytest.skip("cannot find ../c/commontypes.c") 12 | lines = [line for line in f.readlines() if line.strip().startswith('EQ(')] 13 | f.close() 14 | return lines 15 | 16 | def test_alphabetical_order(): 17 | lines = getlines() 18 | assert lines == sorted(lines) 19 | 20 | def test_dependencies(): 21 | r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?') 22 | lines = getlines() 23 | d = {} 24 | for line in lines: 25 | match = r.search(line) 26 | if match is not None: 27 | d[match.group(1)] = match.group(2) 28 | for value in d.values(): 29 | if value: 30 | assert value in d 31 | 32 | def test_get_common_types(): 33 | d = {} 34 | _cffi_backend._get_common_types(d) 35 | assert d["bool"] == "_Bool" 36 | -------------------------------------------------------------------------------- /testing/cffi1/test_dlopen_unicode_literals.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | s = """from __future__ import unicode_literals 4 | """ 5 | 6 | with open(os.path.join(os.path.dirname(__file__), 'test_dlopen.py')) as f: 7 | s += f.read() 8 | 9 | exec(compile(s, filename='test_dlopen.py', mode='exec')) 10 | -------------------------------------------------------------------------------- /testing/cffi1/test_pkgconfig.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | import pytest 4 | import cffi.pkgconfig as pkgconfig 5 | from cffi import PkgConfigError 6 | 7 | 8 | def mock_call(libname, flag): 9 | assert libname=="foobarbaz" 10 | flags = { 11 | "--cflags": "-I/usr/include/python3.6m -DABCD -DCFFI_TEST=1 -O42\n", 12 | "--libs": "-L/usr/lib64 -lpython3.6 -shared\n", 13 | } 14 | return flags[flag] 15 | 16 | 17 | def test_merge_flags(): 18 | d1 = {"ham": [1, 2, 3], "spam" : ["a", "b", "c"], "foo" : []} 19 | d2 = {"spam" : ["spam", "spam", "spam"], "bar" : ["b", "a", "z"]} 20 | 21 | pkgconfig.merge_flags(d1, d2) 22 | assert d1 == { 23 | "ham": [1, 2, 3], 24 | "spam" : ["a", "b", "c", "spam", "spam", "spam"], 25 | "bar" : ["b", "a", "z"], 26 | "foo" : []} 27 | 28 | 29 | def test_pkgconfig(): 30 | assert pkgconfig.flags_from_pkgconfig([]) == {} 31 | 32 | saved = pkgconfig.call 33 | try: 34 | pkgconfig.call = mock_call 35 | flags = pkgconfig.flags_from_pkgconfig(["foobarbaz"]) 36 | finally: 37 | pkgconfig.call = saved 38 | assert flags == { 39 | 'include_dirs': ['/usr/include/python3.6m'], 40 | 'library_dirs': ['/usr/lib64'], 41 | 'libraries': ['python3.6'], 42 | 'define_macros': [('ABCD', None), ('CFFI_TEST', '1')], 43 | 'extra_compile_args': ['-O42'], 44 | 'extra_link_args': ['-shared'] 45 | } 46 | 47 | class mock_subprocess: 48 | PIPE = Ellipsis 49 | class Popen: 50 | def __init__(self, cmd, stdout, stderr): 51 | if mock_subprocess.RESULT is None: 52 | raise OSError("oops can't run") 53 | assert cmd == ['pkg-config', '--print-errors', '--cflags', 'libfoo'] 54 | def communicate(self): 55 | bout, berr, rc = mock_subprocess.RESULT 56 | self.returncode = rc 57 | return bout, berr 58 | 59 | def test_call(): 60 | saved = pkgconfig.subprocess 61 | try: 62 | pkgconfig.subprocess = mock_subprocess 63 | 64 | mock_subprocess.RESULT = None 65 | e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") 66 | assert str(e.value) == "cannot run pkg-config: oops can't run" 67 | 68 | mock_subprocess.RESULT = b"", "Foo error!\n", 1 69 | e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") 70 | assert str(e.value) == "Foo error!" 71 | 72 | mock_subprocess.RESULT = b"abc\\def\n", "", 0 73 | e = pytest.raises(PkgConfigError, pkgconfig.call, "libfoo", "--cflags") 74 | assert str(e.value).startswith("pkg-config --cflags libfoo returned an " 75 | "unsupported backslash-escaped output:") 76 | 77 | mock_subprocess.RESULT = b"abc def\n", "", 0 78 | result = pkgconfig.call("libfoo", "--cflags") 79 | assert result == "abc def\n" 80 | 81 | mock_subprocess.RESULT = b"abc def\n", "", 0 82 | result = pkgconfig.call("libfoo", "--cflags") 83 | assert result == "abc def\n" 84 | 85 | if sys.version_info >= (3,): 86 | mock_subprocess.RESULT = b"\xff\n", "", 0 87 | e = pytest.raises(PkgConfigError, pkgconfig.call, 88 | "libfoo", "--cflags", encoding="utf-8") 89 | assert str(e.value) == ( 90 | "pkg-config --cflags libfoo returned bytes that cannot be " 91 | "decoded with encoding 'utf-8':\nb'\\xff\\n'") 92 | 93 | finally: 94 | pkgconfig.subprocess = saved 95 | -------------------------------------------------------------------------------- /testing/cffi1/test_realize_c_type.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | from cffi import cffi_opcode 4 | 5 | 6 | def check(input, expected_output=None, expected_ffi_error=False): 7 | import _cffi_backend 8 | ffi = _cffi_backend.FFI() 9 | if not expected_ffi_error: 10 | ct = ffi.typeof(input) 11 | assert isinstance(ct, ffi.CType) 12 | assert ct.cname == (expected_output or input) 13 | else: 14 | e = pytest.raises(ffi.error, ffi.typeof, input) 15 | if isinstance(expected_ffi_error, str): 16 | assert str(e.value) == expected_ffi_error 17 | 18 | def test_void(): 19 | check("void", "void") 20 | check(" void ", "void") 21 | 22 | def test_int_star(): 23 | check("int") 24 | check("int *") 25 | check("int*", "int *") 26 | check("long int", "long") 27 | check("long") 28 | 29 | def test_noop(): 30 | check("int(*)", "int *") 31 | 32 | def test_array(): 33 | check("int[6]") 34 | 35 | def test_funcptr(): 36 | check("int(*)(long)") 37 | check("int(long)", expected_ffi_error="the type 'int(long)' is a" 38 | " function type, not a pointer-to-function type") 39 | check("int(void)", expected_ffi_error="the type 'int()' is a" 40 | " function type, not a pointer-to-function type") 41 | 42 | def test_funcptr_rewrite_args(): 43 | check("int(*)(int(int))", "int(*)(int(*)(int))") 44 | check("int(*)(long[])", "int(*)(long *)") 45 | check("int(*)(long[5])", "int(*)(long *)") 46 | 47 | def test_all_primitives(): 48 | for name in cffi_opcode.PRIMITIVE_TO_INDEX: 49 | check(name, name) 50 | 51 | def check_func(input, expected_output=None): 52 | import _cffi_backend 53 | ffi = _cffi_backend.FFI() 54 | ct = ffi.typeof(ffi.callback(input, lambda: None)) 55 | assert isinstance(ct, ffi.CType) 56 | if sys.platform != 'win32' or sys.maxsize > 2**32: 57 | expected_output = expected_output.replace('__stdcall *', '*') 58 | assert ct.cname == expected_output 59 | 60 | def test_funcptr_stdcall(): 61 | check_func("int(int)", "int(*)(int)") 62 | check_func("int foobar(int)", "int(*)(int)") 63 | check_func("int __stdcall(int)", "int(__stdcall *)(int)") 64 | check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") 65 | check_func("void __cdecl(void)", "void(*)()") 66 | check_func("void __cdecl foobar(void)", "void(*)()") 67 | check_func("void __stdcall(void)", "void(__stdcall *)()") 68 | check_func("void __stdcall foobar(long, short)", 69 | "void(__stdcall *)(long, short)") 70 | check_func("void(void __cdecl(void), void __stdcall(void))", 71 | "void(*)(void(*)(), void(__stdcall *)())") 72 | 73 | def test_variadic_overrides_stdcall(): 74 | check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") 75 | -------------------------------------------------------------------------------- /testing/cffi1/test_unicode_literals.py: -------------------------------------------------------------------------------- 1 | # 2 | # ---------------------------------------------- 3 | # WARNING, ALL LITERALS IN THIS FILE ARE UNICODE 4 | # ---------------------------------------------- 5 | # 6 | from __future__ import unicode_literals 7 | # 8 | # 9 | # 10 | from _cffi_backend import FFI 11 | 12 | 13 | def test_cast(): 14 | ffi = FFI() 15 | assert int(ffi.cast("int", 3.14)) == 3 # unicode literal 16 | 17 | def test_new(): 18 | ffi = FFI() 19 | assert ffi.new("int[]", [3, 4, 5])[2] == 5 # unicode literal 20 | 21 | def test_typeof(): 22 | ffi = FFI() 23 | tp = ffi.typeof("int[51]") # unicode literal 24 | assert tp.length == 51 25 | 26 | def test_sizeof(): 27 | ffi = FFI() 28 | assert ffi.sizeof("int[51]") == 51 * 4 # unicode literal 29 | 30 | def test_alignof(): 31 | ffi = FFI() 32 | assert ffi.alignof("int[51]") == 4 # unicode literal 33 | 34 | def test_getctype(): 35 | ffi = FFI() 36 | assert ffi.getctype("int**") == "int * *" # unicode literal 37 | assert type(ffi.getctype("int**")) is str 38 | 39 | def test_callback(): 40 | ffi = FFI() 41 | cb = ffi.callback("int(int)", # unicode literal 42 | lambda x: x + 42) 43 | assert cb(5) == 47 44 | -------------------------------------------------------------------------------- /testing/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import sys 3 | 4 | from ctypes import util 5 | 6 | # this problem was supposedly fixed in a newer Python 3.8 release, but after binary installer support expired 7 | # https://github.com/python/cpython/pull/28054 8 | if sys.platform == 'darwin' and sys.version_info[:2] == (3, 8): 9 | orig_find_library = util.find_library 10 | 11 | def hacked_find_library(*args, **kwargs): 12 | res = orig_find_library(*args, **kwargs) 13 | 14 | if res is None: 15 | pytest.xfail("busted find_library on MacOS Python 3.8") 16 | 17 | return res 18 | 19 | util.find_library = hacked_find_library 20 | -------------------------------------------------------------------------------- /testing/embedding/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/python-cffi/cffi/2b81170583a898478a6996c9705d8c38ab7f73a1/testing/embedding/__init__.py -------------------------------------------------------------------------------- /testing/embedding/add1-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _MSC_VER 4 | #include 5 | #endif 6 | 7 | extern int add1(int, int); 8 | 9 | 10 | int main(void) 11 | { 12 | int x, y; 13 | x = add1(40, 2); 14 | y = add1(100, -5); 15 | printf("got: %d %d\n", x, y); 16 | #ifdef _MSC_VER 17 | if (x == 0 && y == 0) 18 | Sleep(2000); 19 | #endif 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /testing/embedding/add1.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add1(int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | import sys, time 11 | sys.stdout.write("preparing") 12 | for i in range(3): 13 | sys.stdout.flush() 14 | # Windows: sometimes time.sleep() doesn't sleep at all. 15 | # This appears to occur on recent versions of python only. 16 | t_end = time.time() + 0.19 17 | while time.time() < t_end: 18 | time.sleep(0.2) 19 | sys.stdout.write(".") 20 | sys.stdout.write("\n") 21 | 22 | from _add1_cffi import ffi 23 | 24 | int(ord("A")) # check that built-ins are there 25 | 26 | @ffi.def_extern() 27 | def add1(x, y): 28 | sys.stdout.write("adding %d and %d\n" % (x, y)) 29 | sys.stdout.flush() 30 | return x + y 31 | """) 32 | 33 | ffi.set_source("_add1_cffi", """ 34 | """) 35 | 36 | fn = ffi.compile(verbose=True) 37 | print('FILENAME: %s' % (fn,)) 38 | -------------------------------------------------------------------------------- /testing/embedding/add2-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern int add1(int, int); 4 | extern int add2(int, int, int); 5 | 6 | 7 | int main(void) 8 | { 9 | int x, y; 10 | x = add1(40, 2); 11 | y = add2(100, -5, -20); 12 | printf("got: %d %d\n", x, y); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /testing/embedding/add2.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add2(int, int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | import sys 11 | sys.stdout.write("prepADD2\n") 12 | 13 | assert '_add2_cffi' in sys.modules 14 | m = sys.modules['_add2_cffi'] 15 | import _add2_cffi 16 | ffi = _add2_cffi.ffi 17 | 18 | @ffi.def_extern() 19 | def add2(x, y, z): 20 | sys.stdout.write("adding %d and %d and %d\n" % (x, y, z)) 21 | sys.stdout.flush() 22 | return x + y + z 23 | """) 24 | 25 | ffi.set_source("_add2_cffi", """ 26 | """) 27 | 28 | fn = ffi.compile(verbose=True) 29 | print('FILENAME: %s' % (fn,)) 30 | -------------------------------------------------------------------------------- /testing/embedding/add3.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add3(int, int, int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | from _add3_cffi import ffi 11 | import sys 12 | 13 | @ffi.def_extern() 14 | def add3(x, y, z, t): 15 | sys.stdout.write("adding %d, %d, %d, %d\n" % (x, y, z, t)) 16 | sys.stdout.flush() 17 | return x + y + z + t 18 | """) 19 | 20 | ffi.set_source("_add3_cffi", """ 21 | """) 22 | 23 | fn = ffi.compile(verbose=True) 24 | print('FILENAME: %s' % (fn,)) 25 | -------------------------------------------------------------------------------- /testing/embedding/add_recursive-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _MSC_VER 4 | # define DLLIMPORT __declspec(dllimport) 5 | #else 6 | # define DLLIMPORT extern 7 | #endif 8 | 9 | DLLIMPORT int add_rec(int, int); 10 | DLLIMPORT int (*my_callback)(int); 11 | 12 | static int some_callback(int x) 13 | { 14 | printf("some_callback(%d)\n", x); 15 | fflush(stdout); 16 | return add_rec(x, 9); 17 | } 18 | 19 | int main(void) 20 | { 21 | int x, y; 22 | my_callback = some_callback; 23 | x = add_rec(40, 2); 24 | y = add_rec(100, -5); 25 | printf("got: %d %d\n", x, y); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /testing/embedding/add_recursive.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | extern int (*my_callback)(int); 7 | int add_rec(int, int); 8 | """) 9 | 10 | ffi.embedding_init_code(r""" 11 | from _add_recursive_cffi import ffi, lib 12 | import sys 13 | print("preparing REC") 14 | sys.stdout.flush() 15 | 16 | @ffi.def_extern() 17 | def add_rec(x, y): 18 | print("adding %d and %d" % (x, y)) 19 | sys.stdout.flush() 20 | return x + y 21 | 22 | x = lib.my_callback(400) 23 | print('<<< %d >>>' % (x,)) 24 | """) 25 | 26 | ffi.set_source("_add_recursive_cffi", """ 27 | /* use CFFI_DLLEXPORT: on windows, it expands to __declspec(dllexport), 28 | which is needed to export a variable from a dll */ 29 | CFFI_DLLEXPORT int (*my_callback)(int); 30 | """) 31 | 32 | fn = ffi.compile(verbose=True) 33 | print('FILENAME: %s' % (fn,)) 34 | -------------------------------------------------------------------------------- /testing/embedding/empty-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void initialize_my_empty_cffi(void); 4 | 5 | int main(void) 6 | { 7 | initialize_my_empty_cffi(); 8 | printf("OK\n"); 9 | return 0; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /testing/embedding/empty.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api("") 6 | 7 | ffi.set_source("_empty_cffi", """ 8 | void initialize_my_empty_cffi(void) { 9 | if (cffi_start_python() != 0) { 10 | printf("oops, cffi_start_python() returned non-0\\n"); 11 | abort(); 12 | } 13 | } 14 | """) 15 | 16 | fn = ffi.compile(verbose=True) 17 | print('FILENAME: %s' % (fn,)) 18 | -------------------------------------------------------------------------------- /testing/embedding/initerror.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add1(int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | raise KeyError 11 | """) 12 | 13 | ffi.set_source("_initerror_cffi", """ 14 | """) 15 | 16 | fn = ffi.compile(verbose=True) 17 | print('FILENAME: %s' % (fn,)) 18 | 19 | -------------------------------------------------------------------------------- /testing/embedding/perf-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef PTEST_USE_THREAD 5 | # include 6 | static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; 7 | static pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER; 8 | static int remaining; 9 | #endif 10 | 11 | 12 | extern int add1(int, int); 13 | 14 | 15 | static double time_delta(struct timeval *stop, struct timeval *start) 16 | { 17 | return (stop->tv_sec - start->tv_sec) + 18 | 1e-6 * (stop->tv_usec - start->tv_usec); 19 | } 20 | 21 | static double measure(void) 22 | { 23 | long long i, iterations; 24 | int result; 25 | struct timeval start, stop; 26 | double elapsed; 27 | 28 | add1(0, 0); /* prepare off-line */ 29 | 30 | i = 0; 31 | iterations = 1000; 32 | result = gettimeofday(&start, NULL); 33 | assert(result == 0); 34 | 35 | while (1) { 36 | for (; i < iterations; i++) { 37 | add1(((int)i) & 0xaaaaaa, ((int)i) & 0x555555); 38 | } 39 | result = gettimeofday(&stop, NULL); 40 | assert(result == 0); 41 | 42 | elapsed = time_delta(&stop, &start); 43 | assert(elapsed >= 0.0); 44 | if (elapsed > 2.5) 45 | break; 46 | iterations = iterations * 3 / 2; 47 | } 48 | 49 | return elapsed / (double)iterations; 50 | } 51 | 52 | static void *start_routine(void *arg) 53 | { 54 | double t = measure(); 55 | printf("time per call: %.3g\n", t); 56 | 57 | #ifdef PTEST_USE_THREAD 58 | pthread_mutex_lock(&mutex1); 59 | remaining -= 1; 60 | if (!remaining) 61 | pthread_cond_signal(&cond1); 62 | pthread_mutex_unlock(&mutex1); 63 | #endif 64 | 65 | return arg; 66 | } 67 | 68 | 69 | int main(void) 70 | { 71 | #ifndef PTEST_USE_THREAD 72 | start_routine(0); 73 | #else 74 | pthread_t th; 75 | int i, status; 76 | 77 | add1(0, 0); /* this is the main thread */ 78 | 79 | remaining = PTEST_USE_THREAD; 80 | for (i = 0; i < PTEST_USE_THREAD; i++) { 81 | status = pthread_create(&th, NULL, start_routine, NULL); 82 | assert(status == 0); 83 | } 84 | pthread_mutex_lock(&mutex1); 85 | while (remaining) 86 | pthread_cond_wait(&cond1, &mutex1); 87 | pthread_mutex_unlock(&mutex1); 88 | #endif 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /testing/embedding/perf.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add1(int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | from _perf_cffi import ffi 11 | 12 | @ffi.def_extern() 13 | def add1(x, y): 14 | return x + y 15 | """) 16 | 17 | ffi.set_source("_perf_cffi", """ 18 | """) 19 | 20 | fn = ffi.compile(verbose=True) 21 | print('FILENAME: %s' % (fn,)) 22 | -------------------------------------------------------------------------------- /testing/embedding/test_performance.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from testing.embedding.test_basic import EmbeddingTests 3 | 4 | if sys.platform == 'win32': 5 | import pytest 6 | pytestmark = pytest.mark.skip("written with POSIX functions") 7 | 8 | 9 | class TestPerformance(EmbeddingTests): 10 | def test_perf_single_threaded(self): 11 | perf_cffi = self.prepare_module('perf') 12 | self.compile('perf-test', [perf_cffi], opt=True) 13 | output = self.execute('perf-test') 14 | print('='*79) 15 | print(output.rstrip()) 16 | print('='*79) 17 | 18 | def test_perf_in_1_thread(self): 19 | perf_cffi = self.prepare_module('perf') 20 | self.compile('perf-test', [perf_cffi], opt=True, threads=True, 21 | defines={'PTEST_USE_THREAD': '1'}) 22 | output = self.execute('perf-test') 23 | print('='*79) 24 | print(output.rstrip()) 25 | print('='*79) 26 | 27 | def test_perf_in_2_threads(self): 28 | perf_cffi = self.prepare_module('perf') 29 | self.compile('perf-test', [perf_cffi], opt=True, threads=True, 30 | defines={'PTEST_USE_THREAD': '2'}) 31 | output = self.execute('perf-test') 32 | print('='*79) 33 | print(output.rstrip()) 34 | print('='*79) 35 | 36 | def test_perf_in_4_threads(self): 37 | perf_cffi = self.prepare_module('perf') 38 | self.compile('perf-test', [perf_cffi], opt=True, threads=True, 39 | defines={'PTEST_USE_THREAD': '4'}) 40 | output = self.execute('perf-test') 41 | print('='*79) 42 | print(output.rstrip()) 43 | print('='*79) 44 | 45 | def test_perf_in_8_threads(self): 46 | perf_cffi = self.prepare_module('perf') 47 | self.compile('perf-test', [perf_cffi], opt=True, threads=True, 48 | defines={'PTEST_USE_THREAD': '8'}) 49 | output = self.execute('perf-test') 50 | print('='*79) 51 | print(output.rstrip()) 52 | print('='*79) 53 | -------------------------------------------------------------------------------- /testing/embedding/test_recursive.py: -------------------------------------------------------------------------------- 1 | from testing.embedding.test_basic import EmbeddingTests 2 | 3 | 4 | class TestRecursive(EmbeddingTests): 5 | def test_recursive(self): 6 | add_recursive_cffi = self.prepare_module('add_recursive') 7 | self.compile('add_recursive-test', [add_recursive_cffi]) 8 | output = self.execute('add_recursive-test') 9 | assert output == ("preparing REC\n" 10 | "some_callback(400)\n" 11 | "adding 400 and 9\n" 12 | "<<< 409 >>>\n" 13 | "adding 40 and 2\n" 14 | "adding 100 and -5\n" 15 | "got: 42 95\n") 16 | -------------------------------------------------------------------------------- /testing/embedding/test_thread.py: -------------------------------------------------------------------------------- 1 | from testing.embedding.test_basic import EmbeddingTests 2 | 3 | 4 | class TestThread(EmbeddingTests): 5 | def test_first_calls_in_parallel(self): 6 | add1_cffi = self.prepare_module('add1') 7 | self.compile('thread1-test', [add1_cffi], threads=True) 8 | for i in range(20): 9 | output = self.execute('thread1-test') 10 | assert output == ("starting\n" 11 | "preparing...\n" + 12 | "adding 40 and 2\n" * 10 + 13 | "done\n") 14 | 15 | def _take_out(self, text, content): 16 | assert content in text 17 | i = text.index(content) 18 | return text[:i] + text[i+len(content):] 19 | 20 | def test_init_different_modules_in_different_threads(self): 21 | add1_cffi = self.prepare_module('add1') 22 | add2_cffi = self.prepare_module('add2') 23 | self.compile('thread2-test', [add1_cffi, add2_cffi], threads=True) 24 | for i in range(3): 25 | output = self.execute('thread2-test') 26 | print('='*79) 27 | print(output) 28 | print('='*79) 29 | output = self._take_out(output, "preparing") 30 | output = self._take_out(output, ".") 31 | output = self._take_out(output, ".") 32 | # at least the 3rd dot should be after everything from ADD2 33 | assert output == ("starting\n" 34 | "prepADD2\n" 35 | "adding 1000 and 200 and 30\n" 36 | ".\n" 37 | "adding 40 and 2\n" 38 | "done\n") 39 | 40 | def test_alt_issue(self): 41 | add1_cffi = self.prepare_module('add1') 42 | add2_cffi = self.prepare_module('add2') 43 | self.compile('thread2-test', [add1_cffi, add2_cffi], 44 | threads=True, defines={'T2TEST_AGAIN_ADD1': '1'}) 45 | output = self.execute('thread2-test') 46 | output = self._take_out(output, "adding 40 and 2\n") 47 | assert output == ("starting\n" 48 | "preparing...\n" 49 | "adding -1 and -1\n" 50 | "prepADD2\n" 51 | "adding 1000 and 200 and 30\n" 52 | "done\n") 53 | 54 | def test_load_in_parallel_more(self): 55 | add2_cffi = self.prepare_module('add2') 56 | add3_cffi = self.prepare_module('add3') 57 | self.compile('thread3-test', [add2_cffi, add3_cffi], threads=True) 58 | for i in range(150): 59 | output = self.execute('thread3-test') 60 | for j in range(10): 61 | output = self._take_out(output, "adding 40 and 2 and 100\n") 62 | output = self._take_out(output, "adding 1000, 200, 30, 4\n") 63 | assert output == ("starting\n" 64 | "prepADD2\n" 65 | "done\n") 66 | -------------------------------------------------------------------------------- /testing/embedding/test_tlocal.py: -------------------------------------------------------------------------------- 1 | from testing.embedding.test_basic import EmbeddingTests 2 | 3 | 4 | class TestThreadLocal(EmbeddingTests): 5 | def test_thread_local(self): 6 | tlocal_cffi = self.prepare_module('tlocal') 7 | self.compile('tlocal-test', [tlocal_cffi], threads=True) 8 | for i in range(10): 9 | output = self.execute('tlocal-test') 10 | assert output == "done\n" 11 | -------------------------------------------------------------------------------- /testing/embedding/thread-test.h: -------------------------------------------------------------------------------- 1 | /************************************************************/ 2 | #ifndef _MSC_VER 3 | /************************************************************/ 4 | 5 | 6 | #include 7 | 8 | /* don't include , it is not available on OS/X */ 9 | 10 | typedef struct { 11 | pthread_mutex_t mutex1; 12 | pthread_cond_t cond1; 13 | unsigned int value; 14 | } sem_t; 15 | 16 | static int sem_init(sem_t *sem, int pshared, unsigned int value) 17 | { 18 | assert(pshared == 0); 19 | sem->value = value; 20 | return (pthread_mutex_init(&sem->mutex1, NULL) || 21 | pthread_cond_init(&sem->cond1, NULL)); 22 | } 23 | 24 | static int sem_post(sem_t *sem) 25 | { 26 | pthread_mutex_lock(&sem->mutex1); 27 | sem->value += 1; 28 | pthread_cond_signal(&sem->cond1); 29 | pthread_mutex_unlock(&sem->mutex1); 30 | return 0; 31 | } 32 | 33 | static int sem_wait(sem_t *sem) 34 | { 35 | pthread_mutex_lock(&sem->mutex1); 36 | while (sem->value == 0) 37 | pthread_cond_wait(&sem->cond1, &sem->mutex1); 38 | sem->value -= 1; 39 | pthread_mutex_unlock(&sem->mutex1); 40 | return 0; 41 | } 42 | 43 | 44 | /************************************************************/ 45 | #else 46 | /************************************************************/ 47 | 48 | 49 | /* Very quick and dirty, just what I need for these tests. 50 | Don't use directly in any real code! 51 | */ 52 | 53 | #include 54 | #include 55 | 56 | typedef HANDLE sem_t; 57 | typedef HANDLE pthread_t; 58 | 59 | static int sem_init(sem_t *sem, int pshared, unsigned int value) 60 | { 61 | assert(pshared == 0); 62 | assert(value == 0); 63 | *sem = CreateSemaphore(NULL, 0, 999, NULL); 64 | return *sem ? 0 : -1; 65 | } 66 | 67 | static int sem_post(sem_t *sem) 68 | { 69 | return ReleaseSemaphore(*sem, 1, NULL) ? 0 : -1; 70 | } 71 | 72 | static int sem_wait(sem_t *sem) 73 | { 74 | WaitForSingleObject(*sem, INFINITE); 75 | return 0; 76 | } 77 | 78 | static DWORD WINAPI myThreadProc(LPVOID lpParameter) 79 | { 80 | void *(* start_routine)(void *) = (void *(*)(void *))lpParameter; 81 | start_routine(NULL); 82 | return 0; 83 | } 84 | 85 | static int pthread_create(pthread_t *thread, void *attr, 86 | void *start_routine(void *), void *arg) 87 | { 88 | assert(arg == NULL); 89 | *thread = CreateThread(NULL, 0, myThreadProc, start_routine, 0, NULL); 90 | return *thread ? 0 : -1; 91 | } 92 | 93 | 94 | /************************************************************/ 95 | #endif 96 | /************************************************************/ 97 | -------------------------------------------------------------------------------- /testing/embedding/thread1-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "thread-test.h" 4 | 5 | #define NTHREADS 10 6 | 7 | 8 | extern int add1(int, int); 9 | 10 | static sem_t done; 11 | 12 | 13 | static void *start_routine(void *arg) 14 | { 15 | int x, status; 16 | x = add1(40, 2); 17 | assert(x == 42); 18 | 19 | status = sem_post(&done); 20 | assert(status == 0); 21 | 22 | return arg; 23 | } 24 | 25 | int main(void) 26 | { 27 | pthread_t th; 28 | int i, status = sem_init(&done, 0, 0); 29 | assert(status == 0); 30 | 31 | printf("starting\n"); 32 | fflush(stdout); 33 | for (i = 0; i < NTHREADS; i++) { 34 | status = pthread_create(&th, NULL, start_routine, NULL); 35 | assert(status == 0); 36 | } 37 | for (i = 0; i < NTHREADS; i++) { 38 | status = sem_wait(&done); 39 | assert(status == 0); 40 | } 41 | printf("done\n"); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /testing/embedding/thread2-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "thread-test.h" 4 | 5 | extern int add1(int, int); 6 | extern int add2(int, int, int); 7 | 8 | static sem_t done; 9 | 10 | 11 | static void *start_routine_1(void *arg) 12 | { 13 | int x, status; 14 | x = add1(40, 2); 15 | assert(x == 42); 16 | 17 | status = sem_post(&done); 18 | assert(status == 0); 19 | 20 | return arg; 21 | } 22 | 23 | static void *start_routine_2(void *arg) 24 | { 25 | int x, status; 26 | #ifdef T2TEST_AGAIN_ADD1 27 | add1(-1, -1); 28 | #endif 29 | x = add2(1000, 200, 30); 30 | assert(x == 1230); 31 | 32 | status = sem_post(&done); 33 | assert(status == 0); 34 | 35 | return arg; 36 | } 37 | 38 | int main(void) 39 | { 40 | pthread_t th; 41 | int i, status = sem_init(&done, 0, 0); 42 | assert(status == 0); 43 | 44 | printf("starting\n"); 45 | fflush(stdout); 46 | status = pthread_create(&th, NULL, start_routine_1, NULL); 47 | assert(status == 0); 48 | status = pthread_create(&th, NULL, start_routine_2, NULL); 49 | assert(status == 0); 50 | 51 | for (i = 0; i < 2; i++) { 52 | status = sem_wait(&done); 53 | assert(status == 0); 54 | } 55 | printf("done\n"); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /testing/embedding/thread3-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "thread-test.h" 4 | 5 | extern int add2(int, int, int); 6 | extern int add3(int, int, int, int); 7 | 8 | static sem_t done; 9 | 10 | 11 | static void *start_routine_2(void *arg) 12 | { 13 | int x, status; 14 | x = add2(40, 2, 100); 15 | assert(x == 142); 16 | 17 | status = sem_post(&done); 18 | assert(status == 0); 19 | 20 | return arg; 21 | } 22 | 23 | static void *start_routine_3(void *arg) 24 | { 25 | int x, status; 26 | x = add3(1000, 200, 30, 4); 27 | assert(x == 1234); 28 | 29 | status = sem_post(&done); 30 | assert(status == 0); 31 | 32 | return arg; 33 | } 34 | 35 | int main(void) 36 | { 37 | pthread_t th; 38 | int i, status = sem_init(&done, 0, 0); 39 | assert(status == 0); 40 | 41 | printf("starting\n"); 42 | fflush(stdout); 43 | for (i = 0; i < 10; i++) { 44 | status = pthread_create(&th, NULL, start_routine_2, NULL); 45 | assert(status == 0); 46 | status = pthread_create(&th, NULL, start_routine_3, NULL); 47 | assert(status == 0); 48 | } 49 | for (i = 0; i < 20; i++) { 50 | status = sem_wait(&done); 51 | assert(status == 0); 52 | } 53 | printf("done\n"); 54 | fflush(stdout); /* this is occasionally needed on Windows */ 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /testing/embedding/tlocal-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "thread-test.h" 4 | 5 | #define NTHREADS 10 6 | 7 | 8 | extern int add1(int, int); 9 | 10 | static sem_t done; 11 | 12 | 13 | static void *start_routine(void *arg) 14 | { 15 | int i, x, expected, status; 16 | 17 | expected = add1(40, 2); 18 | assert((expected % 1000) == 42); 19 | 20 | for (i=0; i<10; i++) { 21 | x = add1(50, i); 22 | assert(x == expected + 8 + i); 23 | } 24 | 25 | status = sem_post(&done); 26 | assert(status == 0); 27 | 28 | return arg; 29 | } 30 | 31 | int main(void) 32 | { 33 | pthread_t th; 34 | int i, status = sem_init(&done, 0, 0); 35 | assert(status == 0); 36 | 37 | for (i = 0; i < NTHREADS; i++) { 38 | status = pthread_create(&th, NULL, start_routine, NULL); 39 | assert(status == 0); 40 | } 41 | for (i = 0; i < NTHREADS; i++) { 42 | status = sem_wait(&done); 43 | assert(status == 0); 44 | } 45 | printf("done\n"); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /testing/embedding/tlocal.py: -------------------------------------------------------------------------------- 1 | import cffi 2 | 3 | ffi = cffi.FFI() 4 | 5 | ffi.embedding_api(""" 6 | int add1(int, int); 7 | """) 8 | 9 | ffi.embedding_init_code(r""" 10 | from _tlocal_cffi import ffi 11 | import itertools 12 | try: 13 | import thread 14 | g_seen = itertools.count().next 15 | except ImportError: 16 | import _thread as thread # py3 17 | g_seen = itertools.count().__next__ 18 | tloc = thread._local() 19 | 20 | @ffi.def_extern() 21 | def add1(x, y): 22 | try: 23 | num = tloc.num 24 | except AttributeError: 25 | num = tloc.num = g_seen() * 1000 26 | return x + y + num 27 | """) 28 | 29 | ffi.set_source("_tlocal_cffi", """ 30 | """) 31 | 32 | fn = ffi.compile(verbose=True) 33 | print('FILENAME: %s' % (fn,)) 34 | -------------------------------------------------------------------------------- /testing/embedding/withunicode.py: -------------------------------------------------------------------------------- 1 | import sys, cffi 2 | if sys.version_info < (3,): 3 | u_prefix = "u" 4 | else: 5 | u_prefix = "" 6 | unichr = chr 7 | 8 | 9 | ffi = cffi.FFI() 10 | 11 | ffi.embedding_api(u""" 12 | int add1(int, int); 13 | """) 14 | 15 | ffi.embedding_init_code((""" 16 | import sys, time 17 | for c in %s'""" + unichr(0x00ff) + unichr(0x1234) + unichr(0xfedc) + """': 18 | sys.stdout.write(str(ord(c)) + '\\n') 19 | sys.stdout.flush() 20 | """) % u_prefix) 21 | 22 | ffi.set_source("_withunicode_cffi", """ 23 | """) 24 | 25 | fn = ffi.compile(verbose=True) 26 | print('FILENAME: %s' % (fn,)) 27 | -------------------------------------------------------------------------------- /testing/support.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | from cffi._imp_emulation import load_dynamic 3 | 4 | if sys.version_info < (3,): 5 | __all__ = ['u', 'arraytostring', 'load_dynamic'] 6 | 7 | class U(object): 8 | def __add__(self, other): 9 | return eval('u'+repr(other).replace(r'\\u', r'\u') 10 | .replace(r'\\U', r'\U')) 11 | u = U() 12 | long = long # for further "from testing.support import long" 13 | assert u+'a\x00b' == eval(r"u'a\x00b'") 14 | assert u+'a\u1234b' == eval(r"u'a\u1234b'") 15 | assert u+'a\U00012345b' == eval(r"u'a\U00012345b'") 16 | def arraytostring(a): 17 | return a.tostring() 18 | 19 | else: 20 | __all__ = ['u', 'unicode', 'long', 'arraytostring', 'load_dynamic'] 21 | u = "" 22 | unicode = str 23 | long = int 24 | def arraytostring(a): 25 | return a.tobytes() 26 | 27 | 28 | class StdErrCapture(object): 29 | """Capture writes to sys.stderr (not to the underlying file descriptor).""" 30 | def __enter__(self): 31 | try: 32 | from StringIO import StringIO 33 | except ImportError: 34 | from io import StringIO 35 | self.old_stderr = sys.stderr 36 | sys.stderr = f = StringIO() 37 | if hasattr(sys, '__unraisablehook__'): # work around pytest 38 | self.old_unraisablebook = sys.unraisablehook # on recent CPythons 39 | sys.unraisablehook = sys.__unraisablehook__ 40 | return f 41 | def __exit__(self, *args): 42 | sys.stderr = self.old_stderr 43 | if hasattr(self, 'old_unraisablebook'): 44 | sys.unraisablehook = self.old_unraisablebook 45 | 46 | 47 | class FdWriteCapture(object): 48 | """xxx limited to capture at most 512 bytes of output, according 49 | to the Posix manual.""" 50 | 51 | def __init__(self, capture_fd=2): # stderr by default 52 | if sys.platform == 'win32': 53 | import pytest 54 | pytest.skip("seems not to work, too bad") 55 | self.capture_fd = capture_fd 56 | 57 | def __enter__(self): 58 | import os 59 | self.read_fd, self.write_fd = os.pipe() 60 | self.copy_fd = os.dup(self.capture_fd) 61 | os.dup2(self.write_fd, self.capture_fd) 62 | return self 63 | 64 | def __exit__(self, *args): 65 | import os 66 | os.dup2(self.copy_fd, self.capture_fd) 67 | os.close(self.copy_fd) 68 | os.close(self.write_fd) 69 | self._value = os.read(self.read_fd, 512) 70 | os.close(self.read_fd) 71 | 72 | def getvalue(self): 73 | return self._value 74 | 75 | def _verify(ffi, module_name, preamble, *args, **kwds): 76 | from cffi.recompiler import recompile 77 | from .udir import udir 78 | assert module_name not in sys.modules, "module name conflict: %r" % ( 79 | module_name,) 80 | kwds.setdefault('tmpdir', str(udir)) 81 | outputfilename = recompile(ffi, module_name, preamble, *args, **kwds) 82 | module = load_dynamic(module_name, outputfilename) 83 | # 84 | # hack hack hack: copy all *bound methods* from module.ffi back to the 85 | # ffi instance. Then calls like ffi.new() will invoke module.ffi.new(). 86 | for name in dir(module.ffi): 87 | if not name.startswith('_'): 88 | attr = getattr(module.ffi, name) 89 | if attr is not getattr(ffi, name, object()): 90 | setattr(ffi, name, attr) 91 | def typeof_disabled(*args, **kwds): 92 | raise NotImplementedError 93 | ffi._typeof = typeof_disabled 94 | for name in dir(ffi): 95 | if not name.startswith('_') and not hasattr(module.ffi, name): 96 | setattr(ffi, name, NotImplemented) 97 | return module.lib 98 | 99 | 100 | # For testing, we call gcc with "-Werror". This is fragile because newer 101 | # versions of gcc are always better at producing warnings, particularly for 102 | # auto-generated code. We need here to adapt and silence them as needed. 103 | 104 | if sys.platform == 'win32': 105 | extra_compile_args = [] # no obvious -Werror equivalent on MSVC 106 | else: 107 | if (sys.platform == 'darwin' and 108 | [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): 109 | # assume a standard clang or gcc 110 | extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', 111 | '-Wno-unused-parameter', 112 | '-Wno-unreachable-code'] 113 | # special things for clang 114 | extra_compile_args.append('-Qunused-arguments') 115 | else: 116 | # assume a standard gcc 117 | extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion', 118 | '-Wno-unused-parameter', 119 | '-Wno-unreachable-code'] 120 | 121 | is_musl = False 122 | if sys.platform == 'linux': 123 | try: 124 | from packaging.tags import platform_tags 125 | except ImportError: 126 | pass 127 | else: 128 | tagset = frozenset(platform_tags()) 129 | is_musl = any(t.startswith('musllinux') for t in tagset) 130 | if is_musl and sys.version_info >= (3, 12): 131 | if any(t.startswith('musllinux_1_1_') for t in tagset) and not any(t.startswith('musllinux_1_2_') for t in tagset): 132 | # gcc 9.2.0 in the musllinux_1_1 build container has a bug in its sign-conversion warning detection that 133 | # bombs on the definition of _PyLong_CompactValue under Python 3.12; disable warnings-as-errors for that 134 | # specific error on musl 1.1 135 | extra_compile_args.append('-Wno-error=sign-conversion') 136 | 137 | del platform_tags 138 | -------------------------------------------------------------------------------- /testing/udir.py: -------------------------------------------------------------------------------- 1 | import py 2 | import sys, os, atexit 3 | 4 | 5 | # This is copied from PyPy's vendored py lib. The latest py lib release 6 | # (1.8.1) contains a bug and crashes if it sees another temporary directory 7 | # in which we don't have write permission (e.g. because it's owned by someone 8 | # else). 9 | def make_numbered_dir(prefix='session-', rootdir=None, keep=3, 10 | lock_timeout = 172800, # two days 11 | min_timeout = 300): # five minutes 12 | """ return unique directory with a number greater than the current 13 | maximum one. The number is assumed to start directly after prefix. 14 | if keep is true directories with a number less than (maxnum-keep) 15 | will be removed. 16 | """ 17 | if rootdir is None: 18 | rootdir = py.path.local.get_temproot() 19 | 20 | def parse_num(path): 21 | """ parse the number out of a path (if it matches the prefix) """ 22 | bn = path.basename 23 | if bn.startswith(prefix): 24 | try: 25 | return int(bn[len(prefix):]) 26 | except ValueError: 27 | pass 28 | 29 | # compute the maximum number currently in use with the 30 | # prefix 31 | lastmax = None 32 | while True: 33 | maxnum = -1 34 | for path in rootdir.listdir(): 35 | num = parse_num(path) 36 | if num is not None: 37 | maxnum = max(maxnum, num) 38 | 39 | # make the new directory 40 | try: 41 | udir = rootdir.mkdir(prefix + str(maxnum+1)) 42 | except py.error.EEXIST: 43 | # race condition: another thread/process created the dir 44 | # in the meantime. Try counting again 45 | if lastmax == maxnum: 46 | raise 47 | lastmax = maxnum 48 | continue 49 | break 50 | 51 | # put a .lock file in the new directory that will be removed at 52 | # process exit 53 | if lock_timeout: 54 | lockfile = udir.join('.lock') 55 | mypid = os.getpid() 56 | if hasattr(lockfile, 'mksymlinkto'): 57 | lockfile.mksymlinkto(str(mypid)) 58 | else: 59 | lockfile.write(str(mypid)) 60 | def try_remove_lockfile(): 61 | # in a fork() situation, only the last process should 62 | # remove the .lock, otherwise the other processes run the 63 | # risk of seeing their temporary dir disappear. For now 64 | # we remove the .lock in the parent only (i.e. we assume 65 | # that the children finish before the parent). 66 | if os.getpid() != mypid: 67 | return 68 | try: 69 | lockfile.remove() 70 | except py.error.Error: 71 | pass 72 | atexit.register(try_remove_lockfile) 73 | 74 | # prune old directories 75 | if keep: 76 | for path in rootdir.listdir(): 77 | num = parse_num(path) 78 | if num is not None and num <= (maxnum - keep): 79 | if min_timeout: 80 | # NB: doing this is needed to prevent (or reduce 81 | # a lot the chance of) the following situation: 82 | # 'keep+1' processes call make_numbered_dir() at 83 | # the same time, they create dirs, but then the 84 | # last process notices the first dir doesn't have 85 | # (yet) a .lock in it and kills it. 86 | try: 87 | t1 = path.lstat().mtime 88 | t2 = lockfile.lstat().mtime 89 | if abs(t2-t1) < min_timeout: 90 | continue # skip directories too recent 91 | except py.error.Error: 92 | continue # failure to get a time, better skip 93 | lf = path.join('.lock') 94 | try: 95 | t1 = lf.lstat().mtime 96 | t2 = lockfile.lstat().mtime 97 | if not lock_timeout or abs(t2-t1) < lock_timeout: 98 | continue # skip directories still locked 99 | except py.error.Error: 100 | pass # assume that it means that there is no 'lf' 101 | try: 102 | path.remove(rec=1) 103 | except KeyboardInterrupt: 104 | raise 105 | except: # this might be py.error.Error, WindowsError ... 106 | pass 107 | 108 | # make link... 109 | try: 110 | username = os.environ['USER'] #linux, et al 111 | except KeyError: 112 | try: 113 | username = os.environ['USERNAME'] #windows 114 | except KeyError: 115 | username = 'current' 116 | 117 | src = str(udir) 118 | dest = src[:src.rfind('-')] + '-' + username 119 | try: 120 | os.unlink(dest) 121 | except OSError: 122 | pass 123 | try: 124 | os.symlink(src, dest) 125 | except (OSError, AttributeError, NotImplementedError): 126 | pass 127 | 128 | return udir 129 | 130 | 131 | udir = make_numbered_dir(prefix = 'ffi-') 132 | 133 | 134 | # Windows-only workaround for some configurations: see 135 | # https://bugs.python.org/issue23246 (Python 2.7.9) 136 | if sys.platform == 'win32': 137 | try: 138 | import setuptools 139 | except ImportError: 140 | pass 141 | -------------------------------------------------------------------------------- /tools/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """A simple script to update the version embedded in the source.""" 3 | 4 | import argparse 5 | import pathlib 6 | import re 7 | 8 | from packaging.version import Version 9 | 10 | 11 | def main() -> None: 12 | parser = argparse.ArgumentParser(description=__doc__) 13 | parser.add_argument('version', type=Version, help='version number to embed in source files') 14 | args = parser.parse_args() 15 | version: Version = args.version 16 | 17 | major_minor_version = f'{version.major}.{version.minor}' 18 | version_info = ', '.join(map(repr, get_version_info(version))) 19 | 20 | updates: list[tuple[str, re.Pattern, str | Version]] = [ 21 | ('doc/source/conf.py', re.compile(r"^(version = ')[^']*(')$", flags=re.MULTILINE), major_minor_version), 22 | ('doc/source/conf.py', re.compile(r"^(release = ')[^']*(')$", flags=re.MULTILINE), version), 23 | ('setup.py', re.compile(r"^( +version=')[^']*(',)$", flags=re.MULTILINE), version), 24 | ('src/c/_cffi_backend.c', re.compile(r'^(#define CFFI_VERSION +")[^"]*(")$', flags=re.MULTILINE), version), 25 | ('src/c/test_c.py', re.compile(r'^(assert __version__ == ")[^"]*(", .*)$', flags=re.MULTILINE), version), 26 | ('src/cffi/__init__.py', re.compile(r'^(__version__ = ")[^"]*(")$', flags=re.MULTILINE), version), 27 | ('src/cffi/__init__.py', re.compile(r'^(__version_info__ = \()[^)]*(\))$', flags=re.MULTILINE), version_info), 28 | ('src/cffi/_embedding.h', re.compile(r'^( +"\\ncompiled with cffi version: )[^"]*(")$', flags=re.MULTILINE), version), 29 | ] 30 | 31 | repo_root = pathlib.Path(__file__).parent.parent 32 | 33 | for relative_path, pattern, replacement in updates: 34 | path = repo_root / relative_path 35 | original_content = path.read_text() 36 | 37 | if not pattern.search(original_content): 38 | raise RuntimeError(f'{relative_path}: no match found for pattern: {pattern.pattern}') 39 | 40 | updated_content = pattern.sub(rf'\g<1>{replacement}\g<2>', original_content) 41 | 42 | if updated_content == original_content: 43 | print(f'{relative_path}: unchanged') 44 | else: 45 | path.write_text(updated_content) 46 | print(f'{relative_path}: updated') 47 | 48 | 49 | def get_version_info(version: Version) -> tuple: 50 | """Return a tuple representing the given version.""" 51 | version_info = list(version.release) 52 | 53 | if version.pre is not None: 54 | version_info.append(''.join(map(str, version.pre))) 55 | 56 | if version.post is not None: 57 | version_info.append(f'post{version.post}') 58 | 59 | if version.dev is not None: 60 | version_info.append(f'dev{version.dev}') 61 | 62 | if version.local is not None: 63 | version_info.append(f'+{version.local}') 64 | 65 | return tuple(version_info) 66 | 67 | 68 | if __name__ == '__main__': 69 | main() 70 | --------------------------------------------------------------------------------