I think the problem is your misconception, that distutils
will build the executable for you. That is not the case.
Out goal is to use some python functionality from a C-program. Let's do the necessary steps by ourselves:
- Using cython to produce
hello.h
and hello.c
from the given pyx-file.
- Using the created
hello.h
for importing the python functionality in a C-program (main.c
).
- Using a compiler to compile both files
hello.c
and main.c
.
- Using a linker to link the in the last step created object files to an executable.
The first step is easy:
#hello.pyx:
cdef public void say_hello():
print("Hello World")
>>>python -m cython hello.pyx
Now we have hello.c
and hello.h
in our working directory. Your main.c
is wrong and should look like the following:
//main.c
#include <Python.h> //needed
#include "hello.h"
int main(void){
Py_Initialize(); //Needed!
inithello(); //Needed! called PyInit_hello() for Python3
say_hello();
Py_Finalize(); //Needed!
}
Important: If you use Python>=3.5 and Cython>=0.29 you must take multi-phase module initialisation into account, as described in this SO-post - otherwise the resulting program will crash.
I'm not sure why the result of the cython doesn't include Python.h
(this would make it self-contained), but this is the way it is - you need to include it prior to hello.h
. Also Py_Initialize()
and Py_Finalize()
should be called before and after the usage of the functionality from hello.h
.Also you need to initialize the module hello
with inithello()
otherwise a segmentation fault will haunt you at the start of the executable.
Now we need to find the flags for compiling. This can be done with the utility /usr/bin/python-config
(I use python2.7, you need to do the same for python3) and option --cflags
:
>>> /usr/bin/python-config --cflags
-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7
-fno-strict-aliasing -Wdate-time -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong
-Wformat -Werror=format-security -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes
So let's build it:
>>>gcc -c hello.c -o hello.o <our cflags>
>>>gcc -c main.c -o main.o <our cflags>
Now we have both object files hello.o
and main.o
in our working directory. We need to link them and in order to find the right flags we use once again the python-config
utility but this time with the --ldflags
-option:
>>> /usr/bin/python-config --ldflags
--L/usr/lib/python2.7/config-x86_64-linux-gnu -L/usr/lib -lpython2.7
-lpthread -ldl -lutil -lm -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
That means:
>>> gcc main.o hello.o -o prog <our ldflags>
And now we have finally our executable!
Actually, it is not exactly what is asked for, but there is another possibility to generate an executable from the cython code, by using the option --embed
. With this switch on not only the module is cythonized but also a main-function is created. For this your hello.pyx
could look as follows:
#hello.pyx:
cdef public void say_hello():
print("Hello World")
##main:
say_hello()
It is possible also to use __name__=="__main__"
trick, but not needed.
Now after running:
>>>python -m cython hello.pyx --embed
a main
function is created in the resulting hello.c
which takes care of setting up/initializing the python-environment. So we just can build and link it:
>>> gcc hello.c -o prog <our cflags> <our ldflags>
And we are done - no need to know, how to initialize the whole python-stuff right!