Using Craftr for Cython projects

Craftr has convenient support for compiling Cython projects. The easy way is to use compile_project().

from craftr import *
from craftr.ext.compiler import cython

cython.cythonc.compile_project(
  sources = path.glob('src/*.pyx'),
  python_bin = options.get('PYTHON', 'python'),
  additional_flags = ['-Xprofile=True'],
)

For more control, the Cython invocation and C/C++ source file compiling can be done manually. Below is the equivalent long version of the above shorthand:

# craftr_module(cython_test)
from craftr import *
from craftr.ext import platform, python
from craftr.ext.compiler import cython

# 1. Find the compilation information for the target Python version.
py = cython.PythonInfo(options.get('PYTHON', 'python'))

# 2. Compile the .pyx files to C-files.
pyxc_sources = cython.cythonc.compile(
  py_sources = path.glob('src/*.pyx'),
  python_version = py.major_version,
  cpp = False,
  additional_flags = ['-Xprofile=True']
)

# 3. Compile each C file to a shared library.
for pyxfile, cfile in zip(pyxc_sources.inputs, pyxc_sources.outputs):
  platform.ld.link(
    output = path.setsuffix(pyxfile, py.conf['SO']),
    output_type = 'dll',
    keep_suffix = True, # don't let link() replace the suffix
    inputs = platform.cc.compile(
      sources = [cfile],
      frameworks = [py.fw],
      pic = True
    )
  )

Compiling with --embed

Cython has an --embed command-line option that will cause the generated C/C++ source code to contain a main() entry point. You can just pass the main parameter to compile_project() and it will automatically generate an executable:

from craftr import *
from craftr.ext import rules
from craftr.ext.compiler import cython

project = cython.cythonc.compile_project(
  main = path.local('main.pyx'),
  python_bin = options.get('PYTHON', 'python'),
)

# Allows you to invoke `craftr .run` to compile and run
run = rules.run(project.main_bin)

Note

You can combine compiling C-Extensions and an executable in a single call to compile_project().