Commit ddc68024 authored by Mark Lodato's avatar Mark Lodato

cython_freeze for making stand-alone programs

parent 800d96eb
CC = gcc
CYTHON = ./../bin/cython
CYTHON_FREEZE = ../../bin/cython_freeze.py
CFLAGS = -fPIC -g -O2 -Wall -Wextra
CPPFLAGS = -I /usr/include/python2.6
LDFLAGS = -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
LDLIBS = /usr/lib/python2.6/config/libpython2.6.a \
-lm -ldl -pthread -lutil -lz
# Name of executable
TARGET = nCr
# List of Cython source files, with main module first.
CYTHON_SOURCE = combinatorics.pyx cmath.pyx
all : $(TARGET)
$(TARGET) : $(TARGET).o $(CYTHON_SOURCE:.pyx=.o)
$(TARGET).c :
$(CYTHON_FREEZE) $(CYTHON_SOURCE:.pyx=) > $@
%.c : %.pyx
$(CYTHON) $(CYTHONFLAGS) $^
clean:
$(RM) *.o *.c $(TARGET)
.PHONY: clean
.SECONDARY: $(CYTHON_SOURCE:.pyx=.c)
NAME
====
cython_freeze.py - create a C file for embedding Cython modules
SYNOPSIS
========
cython_freeze.py module [...]
DESCRIPTION
===========
**cython_freeze.py** generates a C source file to embed a Python interpreter
with one or more Cython modules built in. This allows one to create a single
executable from Cython code, without having to have separate shared objects
for each Cython module.
A major advantage of this approach is that it allows debuging with gprof(1),
which does not work with shared objects.
Note that this method differs from ``cython --embed``. The ``--embed`` options
modifies the resulting C source file to include a ``main()`` function, so it
can only be used on a single Cython module. The advantage ``--embed`` is
simplicity. This module, on the other hand, can be used with multiple
modules, but it requires another C source file to be created.
EXAMPLE
=======
In the example directory, there exist two Cython modules:
cmath.pyx
A module that interfaces with the -lm library.
combinatorics.pyx
A module that implements n-choose-r using cmath.
Both modules have the Python idiom ``if __name__ == "__main__"``, which only
execute if that module is the "main" module. If run as main, cmath prints the
factorial of the argument, while combinatorics prints n-choose-r.
The provided Makefile creates an executable, *nCr*, using combinatorics as the
"main" module. It basically performs the following (ignoring the compiler
flags)::
$ cython_freeze.py combintorics cmath > nCr.c
$ cython combinatorics.pyx
$ cython cmath.pyx
$ gcc nCr.c -o nCr.o
$ gcc combinatorics.c -o combinatorics.o
$ gcc cmath.c -o cmath.o
$ gcc nCr.o combinatorics.o cmath.o -o nCr
Because the combinatorics module was listed first, its ``__name__`` is set
to ``"__main__"``, while cmath's is set to ``"cmath"``. The executable now
contains a Python interpreter and both Cython modules. ::
$ ./nCr
USAGE: ./nCr n r
Prints n-choose-r.
$ ./nCr 15812351235 12
5.10028093999e+113
PREREQUISITES
=============
Cython 0.11.2 (or newer, assuming the API does not change)
SEE ALSO
========
* `Python <http://www.python.org>`_
* `Cython <http://www.cython.org>`_
* `freeze.py <http://wiki.python.org/moin/Freeze>`_
cdef extern from "math.h":
double c_lgamma "lgamma" (double)
double c_exp "exp" (double)
def exp(n):
"""Return e**n."""
return c_exp(n)
def lfactorial(n):
"""Return an estimate of the log factorial of n."""
return c_lgamma(n+1)
def factorial(n):
"""Return an estimate of the factorial of n."""
return c_exp( c_lgamma(n+1) )
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
sys.stderr.write("USAGE: %s n\nPrints n!.\n" % sys.argv[0])
sys.exit(1)
n = map(float, sys.argv[1:])
print factorial(n)
import cmath
def nCr(n, r):
"""Return the number of ways to choose r elements of a set of n."""
return cmath.exp( cmath.lfactorial(n) - cmath.lfactorial(r)
- cmath.lfactorial(n-r) )
if __name__ == "__main__":
import sys
if len(sys.argv) != 3:
sys.stderr.write("USAGE: %s n r\nPrints n-choose-r.\n" % sys.argv[0])
sys.exit(1)
n, r = map(float, sys.argv[1:])
print nCr(n, r)
#!/usr/bin/env python
"""
Create a C file for embedding one or more Cython source files.
Requires Cython 0.11.2 (or perhaps newer).
See README.rst for more details.
"""
import sys
if len(sys.argv) < 2:
print >>sys.stderr, "USAGE: %s module [module ...]" % sys.argv[0]
sys.exit(1)
def format_modname(name):
if name.endswith('.pyx'):
name = name[:-4]
return name.replace('.','_')
modules = [format_modname(x) for x in sys.argv[1:]]
print """
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#if PY_MAJOR_VERSION < 3
# define MODINIT(name) init ## name
#else
# define MODINIT(name) PyInit_ ## name
#endif
"""
for name in modules:
print "PyMODINIT_FUNC MODINIT(%s) (void);" % name
print """
static struct _inittab inittab[] = {"""
for name in modules:
print ' {"%(name)s", MODINIT(%(name)s)},' % {'name' : name}
print """ {NULL, NULL}
};
extern int __pyx_module_is_main_%(main)s;
#if PY_MAJOR_VERSION < 3 || (!defined(WIN32) && !defined(MS_WINDOWS))
int main(int argc, char** argv) {
#else
int wmain(int argc, wchar_t **argv) {
#endif
int r = 0;
PyObject *m = NULL;
if (PyImport_ExtendInittab(inittab)) {
fprintf(stderr, "No memory\\n");
exit(1);
}
Py_SetProgramName(argv[0]);
Py_Initialize();
PySys_SetArgv(argc, argv);
__pyx_module_is_main_%(main)s = 1;
m = PyImport_ImportModule(inittab[0].name);
if (!m) {
r = 1;
PyErr_Print(); /* This exits with the right code if SystemExit. */
if (Py_FlushLine()); PyErr_Clear();
}
Py_XDECREF(m);
Py_Finalize();
return r;
}
""" % {'main' : modules[0]}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment