Native Libraries¶
bezier
has optional speedups implemented in Fortran.
These are incorporated into the Python interface via
Cython.
The subroutines provided there can be called from Fortran,
C, C++, Cython and any other language that can invoke
a foreign C function (e.g. Go via cgo
).
After bezier
has been installed with these speedups,
the library provides helpers to make it easier to build
code that depends on them.
C Headers¶
The C headers for libbezier
will be included in the source-tree
>>> include_directory = bezier.get_include()
>>> include_directory
'.../site-packages/bezier/include'
>>> print_tree(include_directory)
include/
bezier/
curve.h
curve_intersection.h
helpers.h
surface.h
bezier.h
Note that this includes a catch-all bezier.h
that just includes all of
the headers.
Cython .pxd
Declarations¶
In addition to the header files, several cimport
-able .pxd
Cython declaration files are provided:
>>> bezier_directory = parent_directory(include_directory)
>>> bezier_directory
'.../site-packages/bezier'
>>> print_tree(bezier_directory, suffix='.pxd')
bezier/
_curve.pxd
_curve_intersection.pxd
_helpers.pxd
_surface.pxd
For example, cimport bezier._curve
will provide all the functions
in bezier/curve.h
.
Static Library¶
The actual library libbezier
is included as a single static library
(a .lib
file on Windows and a .a
file elsewhere):
>>> lib_directory = bezier.get_lib()
>>> lib_directory
'.../site-packages/bezier/lib'
>>> print_tree(lib_directory)
lib/
libbezier.a
Note
A static library is used (rather than a shared or dynamic library)
because the “final” install location of the Python package is not
dependable. Even on the same machine with the same operating system,
bezier
can be installed in virtual environments, in different
Python versions, as an egg or wheel, and so on.
Warning
When bezier
is installed via pip, it will likely be installed
from a Python wheel. These wheels will be pre-built and the Fortran
extensions will be compiled with GNU Fortran (gfortran
). As a
result, libbezier
will depend on libgfortran
.
This can be problematic due to version conflicts, ABI incompatibility,
a desire to use a different Fortran compiler (e.g. ifort
) and a host
of other reasons. Some of the standard tooling for building wheels
will try to address this by adding a bezier/.libs
directory with a
version of libgfortran
that is compatible with libbezier
, e.g.
.../site-packages/bezier/.libs/libgfortran-ed201abd.so.3.0.0
If present, this directory can be used when linking. If that is not
feasible, then bezier
can be built from source via:
$ python setup.py build_ext
$ # OR
$ python setup.py build_ext --fcompiler=${FC}
By providing a filename via an environment variable, a “journal” can be stored of the compiler commands invoked to build the extension:
$ export BEZIER_JOURNAL=path/to/journal.txt
$ python setup.py build_ext
$ unset BEZIER_JOURNAL
Building a Python Extension¶
To incorporate libbezier
into a Python extension, either via
Cython, C, C++ or some other means, simply include the header
and library directories:
>>> import setuptools
>>>
>>> extension = setuptools.Extension(
... 'wrapper',
... ['wrapper.c'],
... include_dirs=[
... bezier.get_include(),
... ],
... libraries=['bezier'],
... library_dirs=[
... bezier.get_lib(),
... ],
... )
>>> extension
<setuptools.extension.Extension('wrapper') at 0x...>
Typically, depending on libbezier
implies (transitive) dependence on
libgfortran
. See the warning in Static Library for more details.