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.