Author: Armin Rigo <ar...@tunes.org> Branch: cffi-1.0 Changeset: r2020:1fdf633339c4 Date: 2015-05-17 09:23 +0200 http://bitbucket.org/cffi/cffi/changeset/1fdf633339c4/
Log: in-progress diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst --- a/doc/source/cdef.rst +++ b/doc/source/cdef.rst @@ -2,37 +2,107 @@ Preparing and Distributing modules ====================================== +There are three or four different ways to use CFFI in a project. +In order of complexity: -The minimal versus the extended FFI class ------------------------------------------ - -CFFI contains actually two different ``FFI`` classes. The page `Using -the ffi/lib objects`_ describes the minimal functionality. One of -these two classes contains an extended API, described below. - -.. _`Using the ffi/lib objects`: using.html - -The minimal class is what you get with the out-of-line approach when -you say ``from _example import ffi``. The extended class is what you -get when you say instead:: +* The **"in-line", "ABI mode"**:: import cffi ffi = cffi.FFI() + ffi.cdef("C-like declarations") + lib = ffi.dlopen("libpath") -Only the latter kind contains the methods described below, which are -needed to make FFI objects from scratch or to compile them into -out-of-line modules. + # use ffi and lib here -The reason for this split of functionality is that out-of-line FFI -objects can be used without loading at all the ``cffi`` package. In -fact, a regular program using CFFI out-of-line does not need anything -from the ``cffi`` pure Python package at all (but still needs -``_cffi_backend``, a C extension module). +* The **"out-of-line",** but still **"ABI mode",** useful to organize + the code and reduce the import time:: + # in a separate file "package/foo_build.py" + import cffi -Declaring types and functions ------------------------------ + ffi = cffi.FFI() + ffi.set_source("package._foo", None) + ffi.cdef("C-like declarations") + + if __name__ == "__main__": + ffi.compile() + + Running ``python foo_build.py`` produces a file ``_foo.py``, which + can then be imported in the main program:: + + from package._foo import ffi + lib = ffi.dlopen("libpath") + + # use ffi and lib here + +* The **"out-of-line", "API mode"** gives you the most flexibility to + access a C library at the level of C, instead of at the binary + level:: + + # in a separate file "package/foo_build.py" + import cffi + + ffi = cffi.FFI() + ffi.set_source("package._foo", "real C code") # <= + ffi.cdef("C-like declarations with '...'") + + if __name__ == "__main__": + ffi.compile() + + Running ``python foo_build.py`` produces a file ``_foo.c`` and + invokes the C compiler to turn it into a file ``_foo.so`` (or + ``_foo.pyd`` or ``_foo.dylib``). It is a C extension module which + can be imported in the main program:: + + from package._foo import ffi, lib + # no ffi.dlopen() + + # use ffi and lib here + +* Finally, you can (but don't have to) use CFFI's **Distutils** or + **Setuptools integration** when writing a ``setup.py``. For + Distutils (only in out-of-line API mode):: + + # setup.py (requires CFFI to be installed first) + from distutils.core import setup + + import foo_build # possibly with sys.path tricks to find it + + setup( + ..., + ext_modules=[foo_build.ffi.distutils_extension()], + ) + + For Setuptools (out-of-line, but works in ABI or API mode; + recommended):: + + # setup.py (with automatic dependency tracking) + from setuptools import setup + + setup( + ..., + setup_requires=["cffi>=1.0.dev0"], + cffi_modules=["path/to/foo_build.py:ffi"], + install_requires=["cffi>=1.0.dev0"], + ) + +Note that CFFI actually contains two different ``FFI`` classes. The +page `Using the ffi/lib objects`_ describes the common functionality. +This minimum is what you get in the ``from package._foo import ffi`` +lines above. The extended ``FFI`` class is the one you get from +``import cffi; ffi = cffi.FFI()``. It has the same functionality (for +in-line use), but also the extra methods described below (to prepare +the FFI). + +The reason for this split of functionality is that a regular program +using CFFI out-of-line does not need to import the ``cffi`` pure +Python package at all. (Internally it still needs ``_cffi_backend``, +a C extension module that comes with CFFI.) + + +ffi.cdef(): declaring types and functions +----------------------------------------- **ffi.cdef(source)**: parses the given C source. It registers all the functions, types, constants and global variables in @@ -40,7 +110,7 @@ other functions. Before you can access the functions and global variables, you need to give ``ffi`` another piece of information: where they actually come from (which you do with either ``ffi.dlopen()`` or -``ffi.set_source()/ffi.compile()``). +``ffi.set_source()``). .. _`all types listed above`: @@ -80,7 +150,7 @@ The declarations can also contain "``...``" at various places; these are placeholders that will be completed by the compiler. More information -about it in the next section. +about it below in `Letting the C compiler fill the gaps`_. Note that all standard type names listed above are handled as *defaults* only (apart from the ones that are keywords in the C @@ -90,6 +160,10 @@ cases it might fail, notably with the error ``Multiple type specifiers with a type tag``. Please report it as a bug if it does.) +Multiple calls to ``ffi.cdef()`` are possible. Beware that it can be +slow to call ``ffi.cdef()`` a lot of times, a consideration that is +important mainly in in-line mode. + .. versionadded:: 0.8.2 The ``ffi.cdef()`` call takes an optional argument ``packed``: if True, then all structs declared within @@ -103,6 +177,111 @@ section.) +ffi.dlopen(): loading libraries in ABI mode +------------------------------------------- + +``ffi.dlopen(libpath, [flags])``: this function opens a shared library and +returns a module-like library object. Use this when you are fine with +the limitations of ABI-level access to the system. In case of doubt, read +again `ABI versus API`_ in the overview. + +.. _`ABI versus API`: overflow.html#abi-versus-api + +You can use the library object to call the functions previously +declared by ``ffi.cdef()``, to read constants, and to read or write +global variables. Note that you can use a single ``cdef()`` to +declare functions from multiple libraries, as long as you load each of +them with ``dlopen()`` and access the functions from the correct one. + +The ``libpath`` is the file name of the shared library, which can +contain a full path or not (in which case it is searched in standard +locations, as described in ``man dlopen``), with extensions or not. +Alternatively, if ``libpath`` is None, it returns the standard C library +(which can be used to access the functions of glibc, on Linux). + +Let me state it again: this gives ABI-level access to the library, so +you need to have all types declared manually exactly as they were +while the library was made. No checking is done. Mismatches can +cause random crashes. + +Note that only functions and global variables live in library objects; +the types exist in the ``ffi`` instance independently of library objects. +This is due to the C model: the types you declare in C are not tied to a +particular library, as long as you ``#include`` their headers; but you +cannot call functions from a library without linking it in your program, +as ``dlopen()`` does dynamically in C. + +For the optional ``flags`` argument, see ``man dlopen`` (ignored on +Windows). It defaults to ``ffi.RTLD_NOW``. + +This function returns a "library" object that gets closed when it goes +out of scope. Make sure you keep the library object around as long as +needed. (Alternatively, the out-of-line FFIs have a method +``ffi.dlclose(lib)``.) + + +ffi.set_source(): preparing out-of-line modules +----------------------------------------------- + +**ffi.set_source(module_name, c_header_source, [\*\*keywords...])**: +prepare the ffi for producing out-of-line an external module called +``module_name``. *New in version 1.0.* + +``ffi.set_source()`` by itself does not write any file, but merely +records its arguments for later. It can therefore be called before or +after ``ffi.cdef()``. + +In **ABI mode,** you call ``ffi.set_source(module_name, None)``. The +argument is the name (or dotted name inside a package) of the Python +module to generate. In this mode, no C compiler is called. + +In **API mode,** the ``c_header_source`` argument is a string that +will be pasted into the .c file generated. This piece of C code +typically contains some ``#include``, but may also contain more, +like definitions for custom "wrapper" C functions. The goal is that +the .c file can be generated like this:: + + #include <Python.h> + + ...c_header_source... + + ...magic code... + +where the "magic code" is automatically generated from the ``cdef()``. +For example, if the ``cdef()`` contains ``int foo(int x);`` then the +magic code will contain logic to call the function ``foo()`` with an +integer argument, itself wrapped inside some CPython or PyPy-specific +code. + +The keywords arguments to ``set_source()`` control how the C compiler +will be called. They are passed directly to distutils_ or setuptools_ +and include at least ``sources``, ``include_dirs``, ``define_macros``, +``undef_macros``, ``libraries``, ``library_dirs``, ``extra_objects``, +``extra_compile_args`` and ``extra_link_args``. You typically need at +least ``libraries=['foo']`` in order to link with ``libfoo.so`` or +``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The ``sources`` is a +list of extra .c files compiled and linked together (the file +``module_name.c`` is always generated and automatically added as the +first argument to ``sources``). See the distutils documentations for +`more information about the other arguments`__. + +.. __: http://docs.python.org/distutils/setupscript.html#library-options +.. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules +.. _setuptools: https://pythonhosted.org/setuptools/setuptools.html + +An extra keyword argument processed internally is +``source_extension``, defaulting to ``".c"``. The file generated will +be actually called ``module_name + source_extension``. Example for +C++ (but note that there are a few known issues of C-versus-C++ +compatibility left):: + + ffi.set_source("mymodule", ''' + extern "C" { + int somefunc(int somearg) { return real_cpp_func(somearg); } + } + ''', source_extension='.cpp') + + Letting the C compiler fill the gaps ------------------------------------ @@ -116,9 +295,9 @@ * other arguments are checked: you get a compilation warning or error if you pass a ``int *`` argument to a function expecting a ``long *``. -* similarly, most things declared in the ``cdef()`` are checked, to - the best we implemented so far; mistakes give compilation warnings - or errors. +* similarly, most other things declared in the ``cdef()`` are checked, + to the best we implemented so far; mistakes give compilation + warnings or errors. Moreover, you can use "``...``" (literally, dot-dot-dot) in the ``cdef()`` at various places, in order to ask the C compiler to fill @@ -130,7 +309,7 @@ This declaration will be corrected by the compiler. (But note that you can only access fields that you declared, not others.) Any ``struct`` declaration which doesn't use "``...``" is assumed to be exact, but this is - checked: you get an error if it is not. + checked: you get an error if it is not correct. * unknown types: the syntax "``typedef ... foo_t;``" declares the type ``foo_t`` as opaque. Useful mainly for when the API takes and returns @@ -149,7 +328,8 @@ length is completed by the C compiler. (Only the outermost array may have an unknown length, in case of array-of-array.) This is slightly different from "``int n[];``", because the latter - means that the length is not known even to the C compiler. + means that the length is not known even to the C compiler, and thus + no attempt is made to complete it. * enums: if you don't know the exact order (or values) of the declared constants, then use this syntax: "``enum foo { A, B, C, ... };``" @@ -184,113 +364,23 @@ different declarations). -Preparing out-of-line modules ------------------------------ +ffi.compile() etc.: compiling out-of-line modules +------------------------------------------------- -**ffi.set_source(module_name, c_header_source, [\*\*keywords...])**: -prepare the ffi for producing out-of-line an external module called -``module_name``. *New in version 1.0.* +You can use one of the following functions to actually generate the +.py or .c file prepared with ``ffi.set_source()`` and ``ffi.cdef()``. -The final goal is to produce an external module so that ``from -module_name import ffi`` gives a fast-loading, and possibly -C-compiler-completed, version of ``ffi``. This method -``ffi.set_source()`` is typically called from a separate -``*_build.py`` file that only contains the logic to build this -external module. Note that ``ffi.set_source()`` by itself does not -write any file, but merely records its arguments for later. It can be -called before the ``ffi.cdef()`` or after. See examples in the -overview_. +**ffi.compile(tmpdir='.'):** explicitly generate the .py or .c file, +and (in the second case) compile it. The output file is (or are) put +in the directory given by ``tmpdir``. -.. _overview: overview.html +**ffi.emit_python_code(filename):** same as ``ffi.compile()`` in ABI +mode (i.e. checks that ``ffi.compile()`` would have generated a Python +file). The file to write is explicitly named. -The ``module_name`` can be a dotted name, in case you want to generate -the module inside a package. +**ffi.emit_c_code(filename):** generate the given .c file. -The ``c_header_source`` is either some C source code or None. If it -is None, the external module produced will be a pure Python module; no -C compiler is needed, but you cannot use the ``"..."`` syntax in the -``cdef()``. -On the other hand, if ``c_header_source`` is not None, then you can -use ``"..."`` in the ``cdef()``. In this case, you must plan the -``c_header_source`` to be a string containing C code that will be -directly pasted in the generated C "source" file, like this:: - - ...some internal declarations using the '_cffi_' prefix... - - "c_header_source", pasted directly - - ...some magic code to complete all the "..." from the cdef - ...declaration of helper functions and static data structures - ...and some standard CPython C extension module code - -This makes a CPython C extension module (with a tweak to be -efficiently compiled on PyPy too). The ``c_header_source`` should -contain the ``#include`` and other declarations needed to bring in all -functions, constants, global variables and types mentioned in the -``cdef()``. The "magic code" that follows will complete, check, and -describe them as static data structures. When you finally import this -module, these static data structures will be attached to the ``ffi`` -and ``lib`` objects. - -The ``keywords`` arguments are XXXXXXXXX - - -Compiling out-of-line modules ------------------------------ - -Once an FFI object has been prepared, we must really generate the -.py/.c and possibly compile it. There are several ways. - -**ffi.compile(tmpdir='.'):** explicitly generate the .py/.c and (in -the second case) compile it. The output file(s) are in the directory -given by ``tmpdir``. This is suitable for -xxxxxxxxxxxxx - - - -.. _loading-libraries: - -ABI level: Loading libraries ----------------------------- - -``ffi.dlopen(libpath, [flags])``: this function opens a shared library and -returns a module-like library object. Use this when you are fine with -the limitations of ABI-level access to the system. In case of doubt, read -again `ABI versus API`_ in the overview. - -.. _`ABI versus API`: overflow.html#abi-versus-api - -You can use the library object to call the functions previously -declared by ``ffi.cdef()``, to read constants, and to read or write -global variables. Note that you can use a single ``cdef()`` to -declare functions from multiple libraries, as long as you load each of -them with ``dlopen()`` and access the functions from the correct one. - -The ``libpath`` is the file name of the shared library, which can -contain a full path or not (in which case it is searched in standard -locations, as described in ``man dlopen``), with extensions or not. -Alternatively, if ``libpath`` is None, it returns the standard C library -(which can be used to access the functions of glibc, on Linux). - -Let me state it again: this gives ABI-level access to the library, so -you need to have all types declared manually exactly as they were -while the library was made. No checking is done. - -Note that only functions and global variables are in library objects; -types exist in the ``ffi`` instance independently of library objects. -This is due to the C model: the types you declare in C are not tied to a -particular library, as long as you ``#include`` their headers; but you -cannot call functions from a library without linking it in your program, -as ``dlopen()`` does dynamically in C. - -For the optional ``flags`` argument, see ``man dlopen`` (ignored on -Windows). It defaults to ``ffi.RTLD_NOW``. - -This function returns a "library" object that gets closed when it goes -out of scope. Make sure you keep the library object around as long as -needed. (Alternatively, the out-of-line FFIs have a method -``ffi.dlclose()``.) @@ -432,19 +522,6 @@ some advanced macros (see the example of ``getyx()`` in `demo/_curses.py`_). -* ``sources``, ``include_dirs``, - ``define_macros``, ``undef_macros``, ``libraries``, - ``library_dirs``, ``extra_objects``, ``extra_compile_args``, - ``extra_link_args`` (keyword arguments): these are used when - compiling the C code, and are passed directly to distutils_. You - typically need at least ``libraries=['foo']`` in order to link with - ``libfoo.so`` or ``libfoo.so.X.Y``, or ``foo.dll`` on Windows. The - ``sources`` is a list of extra .c files compiled and linked together. See - the distutils documentation for `more information about the other - arguments`__. - -.. __: http://docs.python.org/distutils/setupscript.html#library-options -.. _distutils: http://docs.python.org/distutils/setupscript.html#describing-extension-modules .. _`demo/_curses.py`: https://bitbucket.org/cffi/cffi/src/default/demo/_curses.py .. versionadded:: 0.4 @@ -477,16 +554,6 @@ check. Be sure to have other means of clearing the ``tmpdir`` whenever you change your sources. -.. versionadded:: 0.9 - You can give C++ source code in ``ffi.verify()``: - -:: - - ext = ffi.verify(r''' - extern "C" { - int somefunc(int somearg) { return real_cpp_func(somearg); } - } - ''', source_extension='.cpp', extra_compile_args=['-std=c++11']) .. versionadded:: 0.9 The optional ``flags`` argument has been added, see ``man dlopen`` (ignored @@ -530,3 +597,8 @@ and afterwards we don't check if they set a Python exception, for example. You may work around it, but mixing CFFI with ``Python.h`` is not recommended. + + + + +cffi_modules, now with the *path as a filename*! _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit