summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Lodato <lodatom@gmail.com>2009-08-06 21:20:43 -0700
committerMark Lodato <lodatom@gmail.com>2009-08-06 21:20:43 -0700
commitddc68024fb5d1fffa36fe0983defd1804ff94f25 (patch)
tree20eedc176508e1932389216502d01345641e1afa
parent800d96ebb67193002e8ebf6c67508993757123ab (diff)
downloadcython-ddc68024fb5d1fffa36fe0983defd1804ff94f25.tar.gz
cython_freeze for making stand-alone programs
-rw-r--r--Demos/freeze/Makefile33
-rw-r--r--Demos/freeze/README.rst82
-rw-r--r--Demos/freeze/cmath.pyx24
-rw-r--r--Demos/freeze/combinatorics.pyx14
-rw-r--r--bin/cython_freeze.py73
5 files changed, 226 insertions, 0 deletions
diff --git a/Demos/freeze/Makefile b/Demos/freeze/Makefile
new file mode 100644
index 000000000..dd511e084
--- /dev/null
+++ b/Demos/freeze/Makefile
@@ -0,0 +1,33 @@
+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)
diff --git a/Demos/freeze/README.rst b/Demos/freeze/README.rst
new file mode 100644
index 000000000..cc81722f5
--- /dev/null
+++ b/Demos/freeze/README.rst
@@ -0,0 +1,82 @@
+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>`_
diff --git a/Demos/freeze/cmath.pyx b/Demos/freeze/cmath.pyx
new file mode 100644
index 000000000..cc3f0521e
--- /dev/null
+++ b/Demos/freeze/cmath.pyx
@@ -0,0 +1,24 @@
+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)
diff --git a/Demos/freeze/combinatorics.pyx b/Demos/freeze/combinatorics.pyx
new file mode 100644
index 000000000..f3a23ece5
--- /dev/null
+++ b/Demos/freeze/combinatorics.pyx
@@ -0,0 +1,14 @@
+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)
diff --git a/bin/cython_freeze.py b/bin/cython_freeze.py
new file mode 100644
index 000000000..184c4a89e
--- /dev/null
+++ b/bin/cython_freeze.py
@@ -0,0 +1,73 @@
+#!/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]}